POJ 2392 Space Elevator(贪心+多重背包)
http://poj.org/problem?id=2392
题意:
题意:给定n种积木,每种积木都有一个高度h[i],一个数量num[i],还有一个限制条件,这个积木所在的位置不能高于limit[i],问能叠起的最大高度?
分析:
本题是一道多重背包问题, 不过每个物品的选择不仅仅要受该种物品的数量num[i]限制, 且该物品还受到limit[i]的限制.
这里有一个贪心的结论:
我们每次背包选取物品时都应该优先放置当前limit[i]值最小的积木(可以画个图看看,不过不太好证明该结论). 所以我们首先把所有积木按limit[i]的值进行从小到大的排序, 然后从1编号开始选积木即可.
下面就是多重背包的过程了.
令dp[i][j]==x表示用前i个积木且总的高度<=j时能达到的最大高度为x.
初始化: dp全为0.
对于每种物品, 我们要做两种选择:
1. num[i]*high[i]>=limit[i]时, 做一次完全背包.
2. Num[i]*high[i]<limit[i]时, 需要把当前物品再分类, 然后做多次01背包即可.
最终所求: dp[n][j]的最大值. 其中j遍历[0,limit[n]]内所有数.
注意: 本来按道理dp[i][j]的语义是<=j时, 而不是正好等于j时. 我们直接输出dp[n][limit[n]]即可的. 但是本题有点特殊. 看下面这组数据:
2
5 11 3
8 12 2
对应的最终dp输出为:
i=0 dp[i]=0
i=1 dp[i]=0
i=2 dp[i]=0
i=3 dp[i]=0
i=4 dp[i]=0
i=5 dp[i]=5
i=6 dp[i]=5
i=7 dp[i]=5
i=8 dp[i]=8
i=9 dp[i]=8
i=10 dp[i]=10
i=11 dp[i]=10
i=12 dp[i]=8
为什么会得到上面奇怪的数据呢? 因为当选择第1个物品(high[1]==5)时, 进行的背包过程只做到了11高度就停了, 没有继续到所有数据. 所以最终需要遍历所有dp数据.
AC代码:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=40000+5; int n;//木块种类 struct Node//每种木块 { int high,num,limit; bool operator<(const Node &rhs)const { return limit<rhs.limit; } }nodes[400+5]; int dp[maxn]; //一次01背包过程 void ZERO_ONE_PACK(int cost,int limit) { for(int i=limit;i>=cost;i--) dp[i] = max(dp[i], dp[i-cost]+cost); } //一次完全背包过程 void COMPLETE_PACK(int cost,int limit) { for(int i=cost;i<=limit;i++) dp[i] = max(dp[i], dp[i-cost]+cost); } //一次多重背包过程 void MULTIPLY_PACK(int cost,int limit,int num) { if(cost*num>=limit) { COMPLETE_PACK(cost,limit); return ; } int k=1; while(k<num) { ZERO_ONE_PACK(cost*k,limit); num-=k; k*=2; } ZERO_ONE_PACK(cost*num,limit); } int main() { while(scanf("%d",&n)==1) { //读取输入+排序 for(int i=1;i<=n;i++) scanf("%d%d%d",&nodes[i].high,&nodes[i].limit,&nodes[i].num); sort(nodes+1,nodes+n+1); //初始化dp+递推 memset(dp,0,sizeof(dp)); for(int i=1;i<=n;i++) MULTIPLY_PACK(nodes[i].high, nodes[i].limit, nodes[i].num); //统计结果输出 int ans=0; for(int i=0;i<=nodes[n].limit;i++) ans=max(ans,dp[i]); printf("%d\n",ans); } return 0; }