题意:
735 3 4 125 6 5 3 350 633 4 500 30 6 100 1 5 0 1 735 0 0 3 10 100 10 50 10 10第一行的意思是给一个735大小的背包
然后3表示后面有3种物品,4和125表示,有个物体大小为125,价值为125,有4个
多重背包求解,交的时候忘了注释freopen,狂WA。。二进制解法如下。
#include<iostream> #include<cstdio> #include<algorithm> #include<vector> #include<list> using namespace std; vector<int>coins; void BinaryDistribute(int num,int val)//二进制分解,分解做法见背包九讲,将num个价值val的物体分解 { int k; for(k=1;num-k*2+1>0;k=(k<<1)) { coins.push_back(k*val); } coins.push_back(val*(num-k+1)); } int d[101000]; int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); int maxValue; while(cin>>maxValue) { coins.clear(); int n; cin>>n; while(n--) { int num; int val; cin>>num>>val; BinaryDistribute(num,val); } n=coins.size(); for(int i=0;i<=maxValue;i++) { d[i]=0; } for(int i=0;i<n;i++)//01背包问题 { for(int j=maxValue;j>=coins[i];j--) { if(d[j]<d[j-coins[i]]+coins[i]) { d[j]=d[j-coins[i]]+coins[i]; } } } printf("%d\n",d[maxValue]); } //system("PAUSE"); return 0; }
单调队列解法
虽说单调队列理论上比二进制快,但是我写的这个单调队列用了800多ms,可能是我第一次实现的原因→ →
关于单调队列优化多重背包的算法,网上大部分是含糊不清,推荐一篇我学习的文章
《国家集训队2009论文集浅谈几类背包题》
#include<iostream> #include<algorithm> #include<cstdio> #include<deque> using namespace std; int v[15];// int k[15];//表示物品v[i]的件数 int d[101000];//d[i]表示体积为i的背包装得的最大值 struct T { int index; int used; }; int main() { int MAX_V; while(~scanf("%d",&MAX_V)) { int n; scanf("%d",&n); for(int i=0;i<n;i++) { scanf("%d %d",&k[i],&v[i]); } for(int i=0;i<=MAX_V;i++) { d[i]=0; } for(int i=0;i<n;i++)//处理v[i] { for(int m=0;m<v[i];m++)//处理d[m],d[m+v[i]],d[m+2*v[i]],d[m+3*v[i]]......... { deque<T>Q;//单调队列d[m+k*v[i]]-k*v[i]的递减队列,队列中实际存储的index是m+k*v[i] for(int j=m;j<=MAX_V;j+=v[i]) { int tused=0; while(!Q.empty()&&d[Q.front().index]+v[i]*(k[i]-Q.front().used)<j) { Q.pop_front(); } if(!Q.empty()&&d[j]<d[Q.front().index]+(j/v[i]-Q.front().index/v[i])*v[i]) { d[j]=d[Q.front().index]+(j/v[i]-Q.front().index/v[i])*v[i]; tused=(j-Q.front().index)/v[i]+Q.front().used; } while(!Q.empty()&&d[Q.back().index]-Q.back().index/v[i]*v[i]<=d[j]-j/v[i]*v[i]) { Q.pop_back(); } T t; t.used=tused; t.index=j; Q.push_back(t); } } } int ant=0; for(int i=0;i<=MAX_V;i++) { if(ant<d[i]) { ant=d[i]; } } printf("%d\n",ant); } return 0; }
更快的单调队列
以上那个单调队列,虽然不知道为什么那么慢,但是最近看了一些其他人写的单调队列解法,模仿了一下,速度是47ms
显然快了很多
解法就是虽然同样是单调队列,但是却没有一个真正的队列结构来维护
d[j]的前状态,显然是单调队列中的队首,但是由于计算状态的时候有d[m],d[m+v[i]],d[m+2*v[i]]
每个状态的最优前状态显然是前面的d[j-v[i]]
#include<iostream> #include<algorithm> #include<cstdio> #include<deque> using namespace std; int v[15];// int k[15];//表示物品v[i]的件数 int d[101000];//d[i]表示体积为i的背包装得的最大值 int num[101000]; int main() { int MAX_V; while(~scanf("%d",&MAX_V)) { int n; scanf("%d",&n); for(int i=0;i<n;i++) { scanf("%d %d",&k[i],&v[i]); } for(int i=0;i<=MAX_V;i++) { d[i]=0; } for(int i=0;i<n;i++)//处理v[i] { for(int j=0;j<=MAX_V;j++) { num[j]=0; } for(int m=0;m<v[i];m++)//处理d[m],d[m+v[i]],d[m+2*v[i]],d[m+3*v[i]]......... { for(int j=m+v[i];j<=MAX_V;j+=v[i]) { if(d[j]<d[j-v[i]]+v[i]&&num[j-v[i]]<k[i]) { d[j]=d[j-v[i]]+v[i]; num[j]=num[j-v[i]]+1; } } } } int ant=0; for(int i=0;i<=MAX_V;i++) { if(ant<d[i]) { ant=d[i]; } } printf("%d\n",ant); } return 0; }