状态压缩dp(hdu2167,poj2411)

hdu2167 http://acm.hdu.edu.cn/showproblem.php?pid=2167

给定一个N*N的板子,里面有N*N个数字,选中一些数字,使得和最大

要求任意两个选中的数字不相邻,相邻包括上下,左右和对角线相邻。

由于N<=15,用程序判断了一下,每一行的有效状态<1600个,如果记录这些状态,然后每一行枚举当前行的上一行的状态那么极端下有1600*1600*15的复杂度,TLE

所以卡在这里很久,想不到怎么优化。 然后看了别人的代码知道了用邻接表存储哪两个状态是相容的。这样就不用枚举那么多了。所以就过了。

 1 #include <stdio.h>

 2 #include <string.h>

 3 #include <iostream>

 4 #include <sstream>

 5 using namespace std;

 6 int matrix[15][15];

 7 int dp[1<<15][15],situation[1600],bit[15],head[1000000],e;

 8 struct node

 9 {

10     int v,next;

11 }g[1000000];;

12 int max(const int &a, const int &b)

13 {

14     return a < b ? b : a;

15 }

16 

17 void addEdge(int u, int v)

18 {

19     g[e].v = v;

20     g[e].next = head[u];

21     head[u] = e++;

22 }

23 int count(int i, int ss, int n)//计算状体第i行的状态s所取的数的和

24 {

25     int ret = 0;

26     int j = 0;

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

28     {

29         if(bit[j] & ss)

30             ret += matrix[i][j];

31     }

32     return ret;

33 }

34 bool check(int s, int ss,int n)//检查s和ss是否可容

35 {

36     

37     if(s&ss) return false;

38     if((s<<1)&ss) return false;

39     if((s>>1)&ss) return false;

40     return true;

41 }

42 int main()

43 {

44     int n,i,j,m,s,ss;

45     char str[100];

46     bit[i=0] = 1;

47     for(i=1; i<15; ++i)

48         bit[i] = bit[i-1]<<1;

49     m = 1<<15;

50     for(i=0,j=0; i<m; ++i)

51         if(!(i&(i<<1)))//求出有效的状态

52             situation[j++]= i;

53     while(gets(str))

54     {

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

56         e = 0;

57         memset(head,-1,sizeof(head));

58         n = 0;

59         do

60         {

61             j = 0;

62             stringstream scin(str);

63             while(scin>>matrix[n][j]) j++;

64             n++;

65             gets(str);

66             if(str[0]=='\0') break;

67 

68         }while(true);

69         m = 1<<n;

70         for(i=0;situation[i]<m; ++i)//判断哪两个状体相容,然后用邻接表存储

71             for(j=0; situation[j]<m; ++j)

72                 if(check(situation[i],situation[j],n))

73                     addEdge(i,j);

74         for(i=0; situation[i]<m; ++i)

75             dp[situation[i]][0] = count(0,situation[i],n);

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

77             for(s=0;situation[s]<m; ++s)

78             {

79                 for(j=head[s];j!=-1;j=g[j].next)

80                 {

81                     dp[situation[g[j].v]][i] = max(dp[situation[g[j].v]][i],dp[situation[s]][i-1]+count(i,situation[g[j].v],n));

82                 }

83             }

84         int ans = 0;

85         for(i=0; situation[i]<m; ++i)

86         {

87             ans = max(ans,dp[situation[i]][n-1]);

88         }

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

90     }

91     return 0;

92 }

 poj2411 http://poj.org/problem?id=2411

给定一个N*M的矩形,用1*2的矩阵填充满,问有多少种填充的方法。 N,M<=11

状态压缩dp,时间复杂度是N*(1<<M)*(1<<M),可以使用邻接表存储,哪些状态是相容的,这样子,就不用枚举所有的状态了。

状态是如何转移的呢?

首先第一行的状态必须是:如果有1,那么必须有连续的两个1.即不能有单独的一个1.

这样子是为了,第二行如果竖着放,那么就可以填充第一行的空缺。

怎么判断当前行的状态和上一行的状态不冲突的?详见bool check(int s, int ss)函数注释

 1 #include <stdio.h>

 2 #include <string.h>

 3 #define LL __int64

 4 int n,m;

 5 int e,head[1000000];

 6 LL dp[1<<11][11];

 7 struct node

 8 {

 9     int v,next;

10 }g[1000000];

11 

12 void addEdge(int a, int b)

13 {

14     g[e].v = b;

15     g[e].next = head[a];

16     head[a] = e++;

17 }

18 void swap(int &a, int &b)

19 {

20     int t = a;

21     a = b;

22     b = t;

23 }

24 bool check(int s)

25 {

26     while(s)

27     {

28         if( (s&1) && ((s>>1)&1))

29             s>>=2;

30         else if( (s&1) && !((s>>1)&1))

31             return false;

32         else if(!(s&1))

33             s>>=1;

34         

35     }

36     return true;

37 }

38 bool check(int s, int ss)

39 {

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

41     {

42         if(!(s&1) && !(ss&1)) return false;//上一行该位置为0,那么这一行该位置应该竖着放,否则不相容

43         else if(!(s&1)&&(ss&1)) 

44         {

45             s>>=1;ss>>=1;continue;//上一行为0,这一行竖着放

46         }

47         else if((s&1)&&!(ss&1)) 

48         {

49             s>>=1;ss>>=1;continue;//上一行该位置为1,这一行可以不放

50         }

51         else if((s&1)&&(ss&1))//上一行的该位置和这一行的该位置为1,那么这行的矩形是横着放

52         {

53             s>>=1,ss>>=1;i++;

54             if((s&1)&&(ss&1)) //即下一个位置,上一行和这一行都必须为1

55             {

56                 s>>=1;ss>>=1;continue;

57             }

58             else return false;

59         }

60     }

61     return true;

62 }

63 int main()

64 {    

65     int s,ss,t,i,j;

66     LL ans;

67     while(scanf("%d%d",&n,&m),n)

68     {

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

70         if(n<m) swap(n,m);

71         ans = 0;

72         t = 1<<m;

73         memset(head,-1,sizeof(head));

74         e = 0;

75         for(s=0; s<t; ++s)

76             for(ss=0; ss<t; ++ss)

77                 if(check(s,ss))

78                     addEdge(s,ss);

79         if((n*m)%2==0)

80         {

81             for(s=0; s<t; ++s)

82                 if(check(s))

83                     dp[s][0] = 1;

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

85                 for(s=0; s<t; ++s)

86                     for(j=head[s];j!=-1 && g[j].v<t;j=g[j].next)

87                         dp[g[j].v][i] += dp[s][i-1];

88                         

89             ans = dp[(1<<m)-1][n-1];

90         }

91         printf("%I64d\n",ans);

92     }

93     return 0;

94 }

 

你可能感兴趣的:(poj)