状态压缩dp入门

poj1321 http://poj.org/problem?id=1321

我们可以把棋盘的每一行看做是一个状态,如果某一列放置了棋子,那么就标记为1,否则就标记为0.然后把它看成是一个二进制数,然后转为10进制数,就可以当做数组下标然后进行状态转移了

设dp[i][s] 为处理到第i行时,状态为s的方法数

那么我们枚举第i-1行的所有状态s

dp[i][s] += dp[i-1][s]; //表示第i行不放置棋子的方法数

dp[i][s|(1<<j)] += dp[i-1][s] //表示第i行第j列放置棋子的方法数   (前提是 chess[i][j]=='#' && 状态s第j列没有放过棋子)

 

 1 #include <stdio.h>

 2 #include <string.h>

 3 char chess[9][9];int dp[11][1<<8];

 4 int n,k;

 5 int main()

 6 {

 7     int i,j,s,ss;

 8     while(scanf("%d%d",&n,&k)!=EOF)

 9     {

10         int ans = 0;

11         if(n==-1) return 0;

12         memset(dp,0,sizeof(dp));

13         for(i=1; i<=n; ++i)

14         {

15             scanf("%s",chess[i]);

16         }

17         dp[0][0] = 1;

18         for(i=1; i<=n; ++i)

19             for(s=0; s<(1<<n); ++s)

20             {

21                 

22                 for(j=0; j<n; ++j)

23                     if(chess[i][j]=='#'&&((1<<j)&s)==0)

24                         dp[i][s|1<<j] += dp[i-1][s];//表示第i行第j列放置棋子的方法数  

25                 dp[i][s] += dp[i-1][s]; //表示第i行不放置棋子的方法数

26             }

27         ///for(j=1; j<=n; ++j)

28         for(s=0; s<(1<<n); ++s)

29         {

30             i = s;

31             int cnt = 0 ;

32             for(;i;i-= i&-i)//统计状态s有多少个1,表示放了多少个棋子

33                 cnt ++;

34             if(cnt==k)

35                 ans+= dp[n][s];

36         }

37         printf("%d\n",ans);

38     }

39     return 0;

40 }

 poj3254 http://poj.org/problem?id=3254

本来这一题也想用上面那题的思路做,可是后来发现不行,因为上面那题一行只选择一个位置,而这题一行可以选择多个位置。

那么我们这题可以枚举多个位置,如果和上一行的状态不冲突,那么上一行的状态就能转移到这一行

 1 #include <stdio.h>

 2 #include <string.h>

 3 

 4 int field[13];

 5 int situation[13];

 6 int dp[13][1<<13];

 7 const int MOD = 100000000;

 8 bool isOk(int s)

 9 {

10     if(s&(s>>1)) return false;

11     return  true;

12 }

13 

14 int main()

15 {

16     int n,m,i,j,s,ss,cnt,ans;

17     while(scanf("%d%d",&n,&m)!=EOF)

18     {

19         ans = cnt = 0;

20         memset(dp,0,sizeof(dp));

21         memset(field,0,sizeof(field));

22         for(s=0; s<(1<<m); ++s) if(isOk(s)) situation[cnt++] = s;//记录所有可行的状态,即不能有相邻的1

23         for(i=0; i<n; ++i)

24             for(j=0; j<m; ++j)

25             {

26                 scanf("%d",&s);

27                 field[i] |= (!s)<<j;//这里这样存的含义是,如果situation[] & field[] 不为0,说明状态situation不行

28             }    

29         

30         //dp[i][j] 表示处理完前i行,第j个状态的方法数

31         for(i=0; i<cnt; ++i)

32             if( !(situation[i] & field[0]))

33                 dp[0][i] = 1;

34             

35         for(i=1; i<n; ++i)

36             for(s=0; s<cnt; ++s)

37             {

38                 if(situation[s]&field[i]) continue;//这一行的状态要合法

39                 for(ss=0; ss<cnt; ++ss)

40                 {

41                     if(situation[ss] & situation[s]) continue;//这一行的状态和上一行的状态不合法

42                     dp[i][s] += dp[i-1][ss];//只要上一行的状态ss和这一行的状态s不冲突,那么就能转化为状态s

43                     dp[i][s] %= MOD;

44                 }

45             }

46         for(i=0; i<cnt; ++i)

47                 ans = (ans + dp[n-1][i]) % MOD;

48         printf("%d\n",ans);

49 

50     }

51     return 0;

52 }

poj1185 http://poj.org/problem?id=1185

和上面那题差不多,只是当前行的状态不止和上一行有关,还和上上一行有关,所以数组要多开一维

奇怪的是为什么用scanf("%c");G++ac,C++wa    用scanf("%s")G++和C++都wa

 1 /*

 2 分析:一行的状态不能有 11(连续的两个1)   也能有101(隔一个空然后出现一个1)

 3 dp[i][j][k]  表示第i行状态为j第i-1行状态为k时,最多有多少个炮兵摆放

 4 dp[i][j][k] = max(dp[i][j][k],dp[i-1][k][t]+num[j]);

 5 */

 6 

 7 #include <stdio.h>

 8 #include <string.h>

 9 int situation[11];

10 char matrix[111][11];

11 int map[111];

12 int num[111];

13 int dp[111][100][100];

14 bool isOk(int s)

15 {

16     if(s&(s<<1)) return false;

17     if(s&(s<<2)) return false;

18     return true;

19 }

20 

21 int count(int s)

22 {

23     int ret = 0;

24     for(;s; s-= (s&-s))//统计状态s有多少个1

25         ret++;

26     return ret;

27 }

28 inline int max(const int &a, const int &b)

29 {

30     return a < b ? b : a;

31 }

32 int main()

33 {

34     int n,m,i,j,k,s,cnt,ans,t;

35     char ch;

36     while(scanf("%d%d",&n,&m)!=EOF)

37     {

38         cnt = ans = 0;

39         memset(dp,-1,sizeof(dp));

40         for(s=0; s<(1<<m); ++s) if(isOk(s)) 

41         {

42             num[cnt] = count(s);

43             situation[cnt++] = s;

44         }

45         

46         for(i=0; i<n; ++i)

47         {

48             

49             getchar();

50             map[i] = 0;

51             for(j=0; j<m; ++j)

52             {

53                 scanf("%c",&ch);

54                 if(ch=='H') map[i] |= 1<<j;

55             }

56         }

57         for(i=0; i<cnt; ++i)

58             if(!(situation[i] & map[0]))

59                 dp[0][i][0] = num[i];

60         for(i=1; i<n; ++i)

61             for(j=0; j<cnt; ++j)

62             {

63                 if(situation[j] & map[i]) continue;

64                 for(k=0; k<cnt; ++k)

65                 {

66                     if(situation[j] & situation[k]) continue;

67                     for(t=0 ;t<cnt; ++t)

68                     {

69                         if(situation[j] & situation[t]) continue;

70                         if(dp[i-1][k][t]==-1) continue;

71                         dp[i][j][k] = max(dp[i][j][k],dp[i-1][k][t] + num[j]);

72                     }

73                 }

74             }

75         for(i=0; i<n; ++i)

76             for(j=0; j<cnt; ++j)

77                 for(k=0; k<cnt; ++k)

78                     ans = max(ans,dp[i][j][k]);

79         printf("%d\n",ans);

80     }

81     

82     return 0;

83 }

 hdu3001 http://acm.hdu.edu.cn/showproblem.php?pid=3001

平常的状态压缩,都是某个位置放(1)或者不放(0),所以可以直接用二进制进行压缩
但是这题,每个点可以走两次,那么就可以标记为0,1,2 所以要用三进制进行压缩。
即用一个数组存储每个状态的三进制的每位

 1 #include <stdio.h>

 2 #include <string.h>

 3 const int INF = 1<<30;

 4 int three[11],situation[59049][11],edge[11][11],dp[59049][11];

 5 void init()

 6 {

 7     int i,t,cnt;

 8     three[0] = 1;

 9     for(i=1; i<=10; ++i)

10         three[i] = three[i-1] * 3;

11     for(i=0; i<59049; ++i)

12     {

13          t = i;

14          cnt = 0;

15          while(t)//存储状态的三进制的每一位

16          {

17              situation[i][cnt++] = t % 3;

18              t /= 3;

19          }

20     }

21 }

22 inline int min(const int &a, const int &b)

23 {

24     return a < b ? a : b;

25 }

26 int main()

27 {

28     init();

29     int n,m,i,j,a,b,c,ans,s;

30     while(scanf("%d%d",&n,&m)!=EOF)

31     {

32         

33         for(i=0; i<n; ++i)

34             for(j=0; j<n; ++j)

35                 edge[i][j] = INF;

36         for(i=0; i<n; ++i)

37             for(s=0; s<three[n]; ++s)

38                 dp[s][i] = INF;

39         ans = INF;

40         for(i=0; i<m; ++i)

41         {

42             scanf("%d%d%d",&a,&b,&c);

43             a--,b--;

44             if(edge[a][b] > c)

45                 edge[a][b] = edge[b][a] = c;

46         }

47         for(i=0; i<n; ++i) dp[three[i]][i] = 0;//处于源点的距离为0

48         //因为走过某些城市可以是任意组合的,所以枚举这些状态

49         for(s=0; s<three[n]; ++s)

50         {

51             bool flag = true;

52             for(i=0; i<n; ++i)

53             {

54                 if(situation[s][i]==0) flag = false;//状态s的第i位为0,说明没有走过所有的城市

55                 if(dp[s][i]==INF) continue;

56                 for(j=0; j<n; ++j)

57                 {

58                     if(i==j) continue;

59                     if(edge[i][j]==INF || situation[s][j]==2) continue;

60                     int nextS = s + three[j];

61                     dp[nextS][j] = min(dp[nextS][j],dp[s][i] + edge[i][j]);

62                 }

63             }        

64             if(flag)//走过所有的状态

65             {

66                 for(i=0; i<n; ++i)

67                     ans = min(ans,dp[s][i]);

68             }

69         }

70         if(ans == INF)

71             printf("-1\n");

72         else

73         printf("%d\n",ans);

74     }

75     return 0;

76 }

 

你可能感兴趣的:(压缩)