http://codevs.cn/problem/5429/
把背包体积按 模物品体积 分类
在每个剩余类中使用单调队列
具体点就是
设物品体积为v,价值为w,现在要计算体积模v=0时的价值
设f[i][j] 表示 前i个物品,体积为j时的最大价值
f[i][5v]=max{ f[i-1][4v]+w , f[i-1][3v]+2w , f[i-1][2v]+3w , f[i-1][v]+4w , f[i-1][0]+5w }
f[i][4v]=max{ f[i-1][3v]+w , f[i-1][2v]+2w , f[i-1][v]+3w , f[i-1][0]+4w }
对所有的f[i][j]-j/v*w
f[i][5v]=max{ f[i-1][4v]-4w , f[i-1][3v]-3w , f[i-1][2v]-2w , f[i-1][v]-w , f[i-1][0] }
f[i][4v]=max{ f[i-1][3v]-3w , f[i-1][2v]-2w , f[i-1][v]-w , f[i-1][0] }
即f[i][j]=max{f[i-1][j%v+k*v]-k*w}+j*w
当固定了j%v后,就可以使用单调队列优化了
#include<cstdio> using namespace std; int dp[7001]; int q[7001][2]; int main() { int n,m; scanf("%d%d",&n,&m); int v,w,cnt; int h,t; for(int i=1;i<=n;++i) { scanf("%d%d%d",&v,&w,&cnt); if(m/v<cnt) cnt=m/v; for(int j=0;j<v;++j) { h=0; t=0; for(int k=0;k<=(m-j)/v;++k) { while(h<t && dp[j+k*v]-k*w>q[t-1][0]) t--; while(h<t && q[h][1]+cnt<k) h++; q[t][0]=dp[j+k*v]-k*w; q[t++][1]=k; dp[j+k*v]=q[h][0]+k*w; } } } printf("%d",dp[m]); }