POJ 2923 Relocation 状态压缩DP + 0-1背包

比较综合的题目。

题意:有n个物品,有两辆车载重分别是c1,c2.问需要多少趟(c1,c2一起运一次就算1趟)能把物品运完。
n比较小,只有10,而且需要把所有物品全部运完,便想到状态压缩来保存状态。
首先记录好所有的可行状态,对于状态state能一趟运完。
然后再利用01背包,dp[j],表示已运的状态为j,如果状态j与state[i]不冲突,则可以从状态j运一趟变为j|state[i]。

View Code
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int n,w[11];
int dp[1026];
int state[1026];
bool vis[1026];
int num,sum, c1, c2;
bool judge(int zt)//判断zt这个状态能不能由c1,c2共同运送一次就能完成。
{
    int i, j;
    memset(vis,0,sizeof(vis));
    vis[0]=1;sum=0;
    for(i=0;i<n;i++)
        if(zt&(1<<i))//第i个物品属于这个状态
        {
            sum+=w[i];
            for(j=c1;j>=w[i];j--)//0-1背包
                if(vis[j-w[i]])vis[j]=1;
        }
    for(i=0;i<=c1;i++)
        if(vis[i]&&sum-i<=c2)return 1;//判断c1,c2运送一次就能把这个状态完成。
    return 0;
}
int main()
{
    int cas, i, j, X=1;
    scanf("%d",&cas);
    while(cas--)
    {
        scanf("%d%d%d",&n,&c1,&c2);
        for(i=0;i<n;i++)//注意必须从0开始读入,不然会影响二进制运算。
            scanf("%d",&w[i]);
        num=0;
        for(i=0;i<(1<<n);i++)
            if(judge(i))state[num++]=i;
        memset(dp,127,sizeof(dp));//赋值正无穷,int范围最大值
        dp[0]=0;
        for(i=0;i<num;i++)//dp[i]表示运走二进制组合数为i的货物最少需要几趟,i表示一个状态
            for(j=((1<<n)-1)-state[i];j>=0;j--)
                if(!(j&state[i]))//if这句话可以不写,但它的存在更能体现对dp的理解
                {    
                    dp[j|state[i]]=min(dp[j|state[i]],dp[j]+1);
                }
        printf("Scenario #%d:\n%d\n\n",X++,dp[(1<<n)-1]);
    }
    return 0;
}

 

posted @ 2012-08-14 19:02  To be an ACMan  Views(171)  Comments(0)    收藏  举报