题目大意
n件物品,第i件hi高,有ci件,最高的一件不能超过ai的高度。问最高能堆多高
输入:
第一行,一个n
接下来每一行,为hi,ai,ci
输出,最高堆多高
样例输入:
3
7 40 3
5 23 8
2 52 6
样例输出:
48 (从下到上:3个2号,3个1号,6个3号)
分析:
我们很容易想到要先放限制高度小的,那我们就先按限制高度从小到大排序。
然后我们可以发现,这个和多重背包很像,“物重”“价值”都为hi,数量为ci
设dp[i][j]为用前i件,花费高度j的盒子,最高能堆多高(显然dp[][j]=j/0)。
那么状态转移方程为dp[i][j]=max{dp[i-1][j-k*hi]+hi*k}其中限制条件是(dp[i-1][j-k*hi]!=0或j==k*hi )
但我们发现这个复杂度是不优秀的,我们需要降复杂度。
以下是优化方法{
我们知道,0/1背包和完全背包只需要改变循环方向就行了。
而完全背包从小到大循环就是保证了在更新了标号小的后,计算标号大的时候利用小的就可以使用更新后的值,从而实现“物品无限”。
而我们多重背包是有限的个数,如何在完全背包上动点手脚?
我们在dp[j]相对记录一个usd[j]表示已经用了这个物品几次了。
在保证条件dp[j-hi]!=0或j==hi 和dp[j-hi]+hi>dp[j]时
还需满足usd[j-hi]+1<=ci,再把usd[j]更新成usd[j-hi]+1,这样就能保证该物品使用次数小于等于ci。
最后输出结果小心坑,找到第一个dp[i]不等于0,输出;特判ans==0的情况
代码:
1 #include<set> 2 #include<map> 3 #include<queue> 4 #include<stack> 5 #include<cmath> 6 #include<cstdio> 7 #include<cstring> 8 #include<iostream> 9 #include<algorithm> 10 #define RG register int 11 #define rep(i,a,b) for(RG i=a;i<=b;++i) 12 #define per(i,a,b) for(RG i=a;i>=b;--i) 13 #define ll long long 14 #define inf (1<<29) 15 #define maxn 405 16 #define maxm 40005 17 using namespace std; 18 int n; 19 int dp[maxm],usd[maxm]; 20 struct Dat{ 21 int w,a,c; 22 inline int operator < (const Dat &tt)const{ 23 return a<tt.a; 24 } 25 }p[maxn]; 26 inline int read() 27 { 28 int x=0,f=1;char c=getchar(); 29 while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} 30 while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} 31 return x*f; 32 } 33 int main() 34 { 35 n=read(); 36 int mx=0; 37 rep(i,1,n) p[i].w=read(),p[i].a=read(),p[i].c=read(); 38 sort(p+1,p+1+n); 39 rep(i,1,n) 40 { 41 memset(usd,0,sizeof(usd)); 42 rep(j,p[i].w,p[i].a) 43 if((j==p[i].w||dp[j-p[i].w]) && dp[j-p[i].w]+p[i].w>dp[j] && usd[j-p[i].w]+1<=p[i].c) 44 dp[j]=dp[j-p[i].w]+p[i].w, 45 usd[j]=usd[j-p[i].w]+1; 46 } 47 per(i,p[n].a,1) if(dp[i]){cout<<dp[i];return 0;} 48 puts("0"); 49 return 0; 50 }