题目链接(卡常背包)

朴素的多重背包是: \(f[i][j] = \max\{ f[i-1][j-k*v[i]]+k*w[i] \}\),复杂度 \(O(nV*\sum num_i)\)

可以发现求\(\max\)时有很多值是被重复枚举过的

换一种方程表示形式,对于每个\(v[i]\),设\(j=K*v[i]+r,\quad K=j/v[i],\quad r=j\%v[i]\),即按照\(\%v[i]\)的余数分别进行dp(第二层枚举余数\(r\))

再枚举\(k=0\sim K-1\)(去掉\(k\)个物品\(i\)),那么 \(f[i][j] = \max\{ f[i-1][k*v[i]+r]-k*w[i] \} + K*w[i]\)

里面这一部分可以用单调队列维护。

大体是这个样子(这里\(f\)是\(f[i]\),\(g\)是\(f[i-1]\),\(k\)是指当前的体积为\(k\times v_i+r\),\(r\)是枚举的余数,\(v_i\)是当前物品\(i\)的单位体积,\(num_i\)是\(i\)物品个数,\(val_i\)是物品\(i\)单位价值):

\[f_k=\max\limits_{k-j\leq num_i}\{g_{j}+(k-j)\times val_i\}\\f_k=\max\limits_{k-j\leq num_i}\{g_{j}-j\times val_i\}+k\times val_i
\]

代码就是第一层枚举\(i\),第二层枚举\(r=0\sim v_i-1\),第三层枚举\(k\),更新\(f[i][k\times v_i+r]\),用单调队列优化。


如果你还没有看懂或想明白怎么用单调队列...

单调队列里每次存进去一个\(f[i-1][j\times v_i+r]-j\times val_i\),更新的时候是$$\begin{aligned}f[i][k\times v_i+r]&=q[h]+k\times val_i\&=\max_{k-j\leq num_i}{\ f[i-1][j\times v_i+r]-j\times val_i\ }+k\times val_i\&=\max_{k-j\leq num_i}{\ f[i-1][j\times v_i+r]+(k-j)\times val_i\ }\end{aligned}$$

\(k-j\)实际就是选了多少个物品\(i\)。

复杂度是 \(O(nV)\)。二三层枚举相当于把\(0\sim V\)都跑了一遍且只有一遍。

另外二进制拆分也可做,复杂度 \(O(nV*\sum log(num_i))\)。由于数据随机下 \(num_i\)可能比较小,\(\sum log(num_i)\)是期望 \(O(n)\) 的。

而且这种方法常数更小,所以比单调队列还快。。

另一问最大数量只有5,可以直接 \(V^2\) 暴力。。

注意当前枚举的体积now与数量的限制: 当队首最优值的体积(用的数量 \(k_h\))+当前物品最大体积(个数上限\(num\))<当前枚举的体积(\(k\))时,要弹出队首

另外最后可以直接输出\(f[V]\)?我觉得不太对啊,大概是那\(m\)个物品一定会产生正权值?

卡常是真恶心==

#include <cstdio>
#include <cctype>
#include <algorithm>
#define gc() getchar()
const int N=1e4+5; int n,m,V,f[N],q[N],nm[N]; inline int read()
{
int now=0,f=1;register char c=gc();
for(;!isdigit(c);c=gc()) if(c=='-') f=-1;
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now*f;
} int main()
{
n=read(),m=read(),V=read();
for(int v,w,num,now,tmp,i=1; i<=n; ++i)
{
v=read(),w=read(),num=read();
for(int j=0; j<v; ++j)//r
for(int k=0,h=1,t=0; (now=k*v+j)<=V; ++k)//now:当前体积
{
tmp=f[now]-k*w;
while(h<=t && q[t]<tmp) --t;
nm[++t]=k, q[t]=tmp;
if(nm[h]+num<k) ++h;//每次仅会弹出一个元素
f[now]=q[h]+k*w;
}
}
for(int a,b,c,i=1; i<=m; ++i)
{
a=read(),b=read(),c=read();
for(int j=V; j; --j)
for(int k=0; k<=j; ++k)
f[j]=std::max(f[j],f[j-k]+(a*k+b)*k+c);
}
// int res=f[1];
// for(int i=2; i<=V; ++i) res=std::max(res,f[i]);
printf("%d",f[V]); return 0;
}

洛谷.1782.旅行商的背包(背包DP 单调队列)的更多相关文章

  1. 洛谷P1782 旅行商的背包[多重背包]

    题目描述 小S坚信任何问题都可以在多项式时间内解决,于是他准备亲自去当一回旅行商.在出发之前,他购进了一些物品.这些物品共有n种,第i种体积为Vi,价值为Wi,共有Di件.他的背包体积是C.怎样装才能 ...

  2. 洛谷P1523 旅行商简化版(DP)

    题目: P1523 旅行商简化版 解析 可以看做是两个人同时从西往东走,经过不一样的点,走到最东头的方案数 设\(f[i][j]\)表示一个人走到i,一个人走到j的最短距离(\(i<j\)) 第 ...

  3. 洛谷P1782 旅行商的背包

    传送门啦 这个题不用二进制优化的话根本不行,现学的二进制优化,调了一段时间终于A了,不容易.. 如果不懂二进制优化的话可以去看我那个博客 二进制优化多重背包入口 不想TLE,不要打memset,一定要 ...

  4. 洛谷 P1440 求m区间内的最小值(单调队列)

    题目链接 https://www.luogu.org/problemnew/show/P1440 显然是一道单调队列题目…… 解题思路 对于单调队列不明白的请看这一篇博客:https://www.cn ...

  5. 题解【洛谷P1886】滑动窗口 /【模板】单调队列

    题面 单调队列模板题. 单调队列可以从队首和队尾出队. 队列中的元素大小具有一定的顺序. 具体可参考这一篇题解 #include <bits/stdc++.h> #define itn i ...

  6. 洛谷P2627 [USACO11OPEN]Mowing the Lawn G (单调队列优化DP)

    一道单调队列优化DP的入门题. f[i]表示到第i头牛时获得的最大效率. 状态转移方程:f[i]=max(f[j-1]-sum[j])+sum[i] ,i-k<=j<=i.j的意义表示断点 ...

  7. 洛谷 P2015 二叉苹果树 (树上背包)

    洛谷 P2015 二叉苹果树 (树上背包) 一道树形DP,本来因为是二叉,其实不需要用树上背包来干(其实即使是多叉也可以多叉转二叉),但是最近都刷树上背包的题,所以用了树上背包. 首先,定义状态\(d ...

  8. 洛谷 P1273 有线电视网(树形背包)

    洛谷 P1273 有线电视网(树形背包) 干透一道题 题面:洛谷 P1273 本质就是个背包.这道题dp有点奇怪,最终答案并不是dp值,而是最后遍历寻找那个合法且最优的\(i\)作为答案.dp值存的是 ...

  9. 【洛谷】【动态规划/二维背包】P1855 榨取kkksc03

    [题目描述:] ... (宣传luogu2的内容被自动省略) 洛谷的运营组决定,如果...,那么他可以浪费掉kkksc03的一些时间的同时消耗掉kkksc03的一些金钱以满足自己的一个愿望. Kkks ...

随机推荐

  1. 【转】Python流程控制语句

    [转]Python流程控制语句 人们常说人生就是一个不断做选择题的过程:有的人没得选,只有一条路能走:有的人好一点,可以二选一:有些能力好或者家境好的人,可以有更多的选择:还有一些人在人生的迷茫期会在 ...

  2. python2 与 python3 语法区别

    python2 与 python3 语法区别 概述# 原稿地址:使用 2to3 将代码移植到 Python 3 几乎所有的Python 2程序都需要一些修改才能正常地运行在Python 3的环境下.为 ...

  3. oracle巡检脚本备份

    重做日志生成情况,一天生成日志大小:select round(sum(blocks*block_size)/1024/1024/1024,2) BLOCK from v\$archived_log w ...

  4. 【转】OpenCV—imread读取数据为空

    之前遇到一个很郁闷的问题,因为从用OpenCV2.3.1改成OpenCV2.4.4,开始改用Mat和imread来代替Iplimage和cvLoadImage,出了点小问题:imread读入数据总是为 ...

  5. Android命令Monkey压力测试,详解

    一.Monkey 是什么?Monkey 就是SDK中附带的一个工具. 二.Monkey 测试的目的?:该工具用于进行压力测试. 然后开发人员结合monkey 打印的日志 和系统打印的日志,结局测试中出 ...

  6. spring事物的传播行为及隔离

    关于@Transactional注解: 添加事务注解1.使用 propagation 指定事务的传播行为, 即当前的事务方法被另外一个事务方法调用时如何使用事务, 默认取值为 REQUIRED, 即使 ...

  7. vue-cli 搭建的项目处理不同环境下请求不同域名的问题

    使用 vue-cli 开发项目过程中, 根据开发环境和正式环境不同, 我们往往需要请求不同域名下的后台接口, 这时候, 该怎么去设置, 达到同一种写法可以根据环境不同而自动切换请求域名呢? 本文将会介 ...

  8. cf796d 树,bfs好题!

    绝对是好题,把所有警察局放入队列然后开始广搜,如果碰到了vis过的顶点,但是那条边没有访问过,那么这条边就可以删掉 另外广搜的vis标记是在入队时就打的,, #include<bits/stdc ...

  9. 性能测试二十八:环境部署之Dubbo部署

    Zookeeper部署 ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件.它是一个为分布式应用提供一 ...

  10. python 全栈开发,Day124(MongoDB初识,增删改查操作,数据类型,$关键字以及$修改器,"$"的奇妙用法,Array Object 的特殊操作,选取跳过排序,客户端操作)

    一.MongoDB初识 什么是MongoDB MongoDB 是一个基于分布式文件存储的数据库.由 C++ 语言编写.旨在为 WEB 应用提供可扩展的高性能数据存储解决方案. MongoDB 是一个介 ...