poj 2923 状压dp+01背包

好牛b的思路

题意:一系列物品,用二辆车运送,求运送完所需的最小次数,两辆车必须一起走

解法为状态压缩DP+背包,
本题的解题思路是先枚举选择若干个时的状态,
总状态量为1<<n,判断这些状态集合里的那些物品能否一次就
运走,如果能运走,那就把这个状态看成一个物品。预处理完能
从枚举中找到tot个物品,再用这tol个物品中没有交集
(也就是两个状态不能同时含有一个物品)的物品进
行01背包,每个物品的体积是state[i],价值是1,求
包含n个物品的最少价值也就是dp[(1<<n)-1](dp[i]表示状态i需要运的最少次数)。

状态转移方程:dp[j|k] = min(dp[j|k],dp[k]+1) (k为state[i,1<=j<=(1<<n)-1])。
算法复杂度O((2^N)*N)

 1 #include<stdio.h>

 2 #include<iostream>

 3 #include<string.h>

 4 #include<algorithm>

 5 #include<math.h>

 6 using namespace std;

 7 const int INF=0x3f3f3f3f;

 8 int state[1030];

 9 int tol;

10 int dp[1030];

11 int n,C1,C2;

12 int cost[110];

13 bool vis[1030];

14 

15 bool judge(int x)

16 {

17     int sum=0;

18     memset(vis,false,sizeof(vis));

19     vis[0]=true;

20     for(int i=0;i<n;i++)

21     {

22         if((1<<i)&x)

23         {

24             sum+=cost[i];

25             for(int j=C1;j>=cost[i];j--)

26               if(vis[j-cost[i]])

27                  vis[j]=true;

28         }

29     }

30     if(sum>C1+C2)return false;

31     for(int i=0;i<=C1;i++)

32       if(vis[i]&&sum-i<=C2)

33         return true;

34     return false;

35 }

36 int main()

37 {

38     int T;

39     int iCase=0;

40     scanf("%d",&T);

41     while(T--)

42     {

43         iCase++;

44         scanf("%d%d%d",&n,&C1,&C2);

45         for(int i=0;i<n;i++)

46           scanf("%d",&cost[i]);

47         for(int i=0;i<(1<<n);i++)dp[i]=INF;

48         dp[0]=0;

49         tol=0;

50         for(int i=1;i<(1<<n);i++)

51           if(judge(i))

52             state[tol++]=i;

53         for(int i=0;i<tol;i++)

54           for(int j=(1<<n)-1;j>=0;j--)

55           {

56               if(dp[j]==INF)continue;

57               if((j&state[i])==0)

58               {

59                   dp[j|state[i]]=min(dp[j|state[i]],dp[j]+1);

60               }

61           }

62         printf("Scenario #%d:\n%d\n\n",iCase,dp[(1<<n)-1]);

63     }

64     return 0;

65 }

 

你可能感兴趣的:(poj)