http://poj.org/problem?id=2923
(1)2辆车搬运n件家具,求最少的搬运次数。背包问题的一般思路是,把物品顺次装入背包,不过背包有且仅有一个。然而,这里的“背包”,不仅有两个,
(2辆车),而且“背包”还可以重复使用。。。这种情况下,就不能简单地讲车看做背包,而应该另辟新径。
(2)用到状态压缩(将每种状态用一个整数 x 的二进制形式来表示),先确定所有的状态下哪些情况能够一次运完,做好标记。在初始化dp数组为无穷大后
(dp[0]=0),遍历每种一次可以运完的情况,看看是否可以用它来更新其他状况的dp:
if(!(j&re[i])) dp[j|re[i]]=min(dp[j|re[i]], dp[j]+1);
(3)值得注意的地方(血的代价):
const int maxn=1<<10;
而不是:
const int maxn=120;
对数组开得太小(主要是没有细想),出现了各种错误(TLE,WA,RE),还以为是自己越写越糟了。。
具体代码:
View Code
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; const int maxn=1<<10; const int Inf=1<<28; int t, n, c1, c2; int w[maxn], re[maxn]; int vis[maxn], dp[maxn]; int yes(int x) { for(int i=0;i<=c1;i++) vis[i]=0; vis[0]=1; int sum=0; for(int i=0;i<n;i++) if(x&(1<<i)) { sum+=w[i]; for(int j=c1;j>=w[i];j--) vis[j]=vis[j]|vis[j-w[i]]; } for(int i=c1;i>=0;i--) if(vis[i]&&c2>=sum-i) return 1; return 0; } int main() { int cas=0; while(~scanf("%d", &t)) { while(t--) { scanf("%d%d%d", &n, &c1, &c2); if(c1>c2) swap(c1, c2); for(int i=0;i<n;i++) scanf("%d", &w[i]); int tot=0; for(int i=0;i<(1<<n);i++) dp[i]=Inf; dp[0]=0; for(int i=1;i<(1<<n);i++) if(yes(i)) { re[tot++]=i; } for(int i=0;i<tot;i++) for(int j=(1<<n)-1-re[i];j>=0;j--) if(!(j&re[i])) dp[j|re[i]]=min(dp[j|re[i]], dp[j]+1); printf("Scenario #%d:\n%d\n\n", ++cas, dp[(1<<n)-1]); } } return 0; }
