poj 3254(状态压缩DP)

poj  3254(状态压缩DP)

题意:一个矩阵里有很多格子,每个格子有两种状态,可以放牧和不可以放牧,可以放牧用1表示,否则用0表示,在这块牧场放牛,要求两个相邻的方格不能同时放牛,即牛与牛不能相邻。问有多少种放牛方案(一头牛都不放也是一种方案)

解析:根据题意,把每一行的状态用二进制的数表示,0代表不在这块放牛,1表示在这一块放牛。首先很容易看到,每一行的状态要符合牧场的硬件条件,即牛必须放在能放牧的方格上。这样就能排除一些状态。另外,牛与牛之间不能相邻,这样就要求每一行中不能存在两个相邻的1,这样也能排除很多状态。然后就是根据上一行的状态转移到当前行的状态的问题了。必须符合不能有两个1在同一列(两只牛也不能竖着相邻)的条件。这样也能去掉一些状态。第i行为某状态state时的方案数为第i-1行的所有符合条件的状态的方案数的总和。

状态表示:dp[state][i]:在状态为state时,到第i行符合条件的可以放牛的方案数

状态转移方程:dp[state][i] =Sigma dp[state'][i-1] (state'为符合条件的所有状态)

DP边界条件:首行放牛的方案数dp[state][1] =1(state符合条件) OR 0 (state不符合条件)

利用位运算可以巧妙的判断某些状态是否符合条件:

if(a&(a<<1)==1),用于判断a中相邻位是否同为1,等式成立表示存在,否则不存在;

if(a&b),用于判断a和b相同位是否同为1,等式成立表示存在,否则不存在。

AC代码如下:

 1 #include<stdio.h>

 2 #define M 100000000

 3 int dp[13][1<<13],state[1<<13],cur[13];

 4 int m,n,top=0;

 5 void init()        //预处理所有满足条件(相邻位置不能放牛)的状态

 6 {

 7     int i,sum=1<<n;

 8     for(i=0;i<sum;i++)

 9         if(i&(i<<1))

10             continue;

11         else

12             state[top++]=i;

13 }

14 int fit(int a,int b)   //判断二进制数相同位置是否同为1

15 {

16     if(a&b)

17         return 0;

18     return 1;

19 }

20 void DP()

21 {

22     int i,j,k;

23     for(i=0;i<top;i++)        //初始化第一行

24         if(fit(state[i],cur[1]))

25             dp[1][i]=1;

26     for(i=2;i<=m;i++)

27         for(j=0;j<top;j++)   //遍历第i行所有状态

28         {

29             if(!fit(cur[i],state[j]))     //若该状态不符合条件

30                 continue;

31             else

32             {

33                 for(k=0;k<top;k++)      //遍历第i-1行所有状态,找出满足条件的

34                 {

35                     if(!fit(state[k],cur[i-1]))  //这一句其实不用也可以

36                         continue;

37                     if(!fit(state[k],state[j]))   //若相邻位置同为1,不符合条件

38                         continue;

39                     dp[i][j]=(dp[i][j]+dp[i-1][k])%M;   //dp[state][i] =Sigma dp[state'][i-1] (state'为符合条件的所有状态)

40                 }

41             }

42         }

43     int ans=0;

44     for(i=0;i<top;i++)          //求最后一行所有满足条件的状态

45         ans=(ans+dp[m][i])%M;

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

47 }

48 int main()

49 {

50     int x,i,j;

51     scanf("%d%d",&m,&n);

52     for(i=1;i<=m;i++)

53         for(j=1;j<=n;j++)

54         {

55             scanf("%d",&x);

56             if(x==0)        //如果该位置不能放牛,则将该行变成相应的二进制数,方便判断

57                 cur[i]+=1<<(n-j);

58         }

59     init();

60     DP();

61     return 0;

62 }
View Code

 

你可能感兴趣的:(poj)