POJ 3093 Margaritas on the River Walk 0-1背包好题
给n个物品,容量为m的包,要求这样的方案数:选中n个物品放入背包后,剩下的物品中,任意一个都放不进去。
我们先将物品按容量从大到小排序。
考虑以下2种情况:
1. 考虑一种特殊情况,一个都放不进去,就是剩下物品中体积最小的那个(体积为w[1])都放不进去,这个情况就会无解,答案为0;
2. 一般情况:从小到大枚举不放入背包的体积最小的,那么此时比它小的物品都应该放入背包中,此时背包装载容量应为[m-w[u]+1,m] (1<=u<=n)
我们可以对剩下的u+1到n的物品做01背包,然后将可行的方案统计出来,这样,第u件物品考虑了u-1次,总复杂度为O(n^2*m);
优化:
n较大的话会超时,我们可以反过来做,每个物品只放入背包中一次,即枚举的时候从最大的开始,先统计此时的方案数,然后将物品放入背包,这样一直下去,总复杂度O(n*m) 即背包装载容量应为[m-w[u]+1,m] (1<=u<=n)
一般方法:
View Code
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; int n, m, ans; int w[31], sum[31]; int dp[1003]; int main() { int cas, i, j, u, ca=1; scanf("%d",&cas); while(cas--) { scanf("%d%d",&n,&m); for(i=1;i<=n;i++) scanf("%d",&w[i]); sort(w+1,w+n+1); printf("%d ",ca++); if(w[1]>m){printf("0\n");continue;} sum[0]=0;ans=0; for(i=1;i<=n;i++) sum[i]=sum[i-1]+w[i]; for(u=1;u<=n;u++)//枚举每个不放入背包的体积最小的 { memset(dp,0,sizeof(dp)); dp[0]=1; for(i=u+1;i<=n;i++) for(j=m-sum[u-1];j>=w[i];j--)//前u个物品都放入了背包中 dp[j]+=dp[j-w[i]]; for(j=max(0,m-sum[u-1]-w[u]+1);j<=m-sum[u-1];j++) ans+=dp[j]; } printf("%d\n",ans); } return 0; }
优化:
View Code
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; int sum[35],w[35]; int dp[1005]; int n, m, ans; int main() { int cas, i, j, k, ca=1; scanf("%d",&cas); while(cas--) { scanf("%d%d",&n,&m); for(i=1;i<=n;i++) scanf("%d",&w[i]); sort(w+1,w+1+n); printf("%d ",ca++); if(w[1]>m){printf("0\n");continue;} sum[0]=0; for(i=1;i<=n;i++) sum[i]=sum[i-1]+w[i]; ans=0; memset(dp,0,sizeof(dp)); dp[0]=1; for(i=n;i>=1;i--) { for(j=max(0,m-sum[i-1]-w[i]+1);j<=m-sum[i-1];j++) ans+=dp[j]; for(j=m;j>=w[i];j--) dp[j]+=dp[j-w[i]]; } printf("%d\n",ans); } return 0; }


浙公网安备 33010602011771号