题意:
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;
}