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;
}