【DP专题】——旅行商的背包
链接:https://www.luogu.org/problemnew/show/P1782
个人看来,这道题是一道非常好的多重和完全背包练手题。
题目描述小S坚信任何问题都可以在多项式时间内解决,于是他准备亲自去当一回旅行商。在出发之前,他购进了一些物品。这些物品共有n种,第i种体积为Vi,价值为Wi,共有Di件。他的背包体积是C。怎样装才能获得尽量多的收益呢?作为一名大神犇,他轻而易举的解决了这个问题。 然而,就在他出发前,他又收到了一批奇货。这些货共有m件,第i件的价值Yi与分配的体积Xi之间的关系为:Yi=ai*Xi^2+bi*Xi+ci。这是件好事,但小S却不知道怎么处理了,于是他找到了一位超级神犇(也就是你),请你帮他解决这个问题。 输入输出格式输入格式:
第一行三个数n,m,C,如题中所述; 以下n行,每行有三个数Vi,Wi,Di,如题中所述; 以下m行,每行有三个数ai,bi,ci,如题中所述。 输出格式:
仅一行,为最大的价值。 |
既然谈到背包,那么我们来把基本的几个背包复习一遍:
1.01背包:
不用多说,01背包就是对于一个物品,你选或者不选,选的状态由你选之前的那个状态转移而来。
伪代码:
for:(i) 1->n for: (j) maxv->v[i] f[j]=max ( f[j-v[i]]+w[i] )
2.完全背包:
完全背包考虑到每个物品能装无限个,但实质上,和01背包的区别就是,状态转移不但可以从选之前的那个状态转移而来,还可以是选之后的某个状态(这样相当于重复选择),所以第二层循环要正着写:
for:(i) 1->n for: (j) v[i]->maxv f[j]=max ( f[j-v[i]]+w[i] )
3.多重背包:
多重背包限定了物品数量,所以不能在原来的基础上增加物品,而是另起一个循环,枚举数量。
for:(i) 1->n for: (j) maxv->v[i] for:(k) 1->c[i] (k*v[i]<=j) f[j]=max ( f[j-k*v[i]]+k*w[i] )
其余的背包以后再说。其实是懒得写
所以接下来就有思路了吧!
但是呢这道题数据有点坑人,我们需要在基础上做一些优化。
--单调队列:
考虑用单调队列来优化:
参见滑动窗口
我们在维护最值的时候,可以用单调队列优化。
--读入优化(废话)
#include<iostream> #include<cstdio> using namespace std; const int N=10010; int n,m,cool; int v[N],w[N],d[N]; int a[10],b[10],c[10]; int tietie[N]; int fu[N],sai[N]; int main(){ scanf("%d%d%d",&n,&m,&cool); for(int i=1;i<=n;i++){ scanf("%d%d%d",&v[i],&w[i],&d[i]); } for(int i=1;i<=m;i++){ scanf("%d%d%d",&a[i],&b[i],&c[i]); } for(int i=1;i<=n;i++){ for(int j=0;j<v[i];j++){ for(int k=0,bu=0,ki=0;k*v[i]+j<=cool;k++){ int matsuri=tietie[j+k*v[i]]-k*w[i]; while(bu!=ki&&matsuri>fu[ki-1]) ki--; sai[ki]=k,fu[ki++]=matsuri; if(sai[bu]+d[i]<k) bu++; fu[k*v[i]+j]=fu[bu]+k*w[i]; } } } for(int i=1;i<=m;i++){ for(int j=cool;j>=0;j--){ for(int k=0;k<=j;k++){ if(tietie[j]<tietie[j-k]+(k*a[i]+b[i])*k+c[i]) tietie[j]=tietie[j-k]+(k*a[i]+b[i])*k+c[i]; } } } printf("%d",tietie[cool]); return 0; }
奇怪的是没有过,所以暂时放这了,以后再改。