2016.8.5_状压dp

来源:互联网 发布:c语言输出杨辉三角形 编辑:程序博客网 时间:2024/04/30 17:57

  今天我们讲了状态压缩动态规划,主要就是将多维的状态用二进制压缩起来,降低代码复杂度和时间复杂度。
  举些栗子:

1.POJ3254

  在一个n*m(1<=n,m<=12)的田地中种玉米,有些格子不能放,要求不能有任何两个玉米相邻,问有多少种方法(方法数mod 100000000)。
  看这题第一个想到的方法就是深搜,时间复杂度是O(2^n*m),也就是2^144,很明显是超时的。再看一遍题目,发现第i行的放置方法,只被第i-1行所影响,想到了动规的无后效性,所以可以用动规。
  如果每一行有m个格子,那么第i行的状态可以表示为dp[i][F1][F2]…[Fm],但这样m不同时,数组维度也不同,不好写(也可以写12种dp,太麻烦)。因为这后面的状态都是01,所以就可以用一个二进制将后面所有状态压缩起来,这就实现了状态压缩。
  枚举状态的时候,只要从0到2^m循环就可以了。其他操作可以用位运算来实现。
代码:

#include <iostream>#include <fstream>#include <string.h>using namespace std;const int MAXSTATE = 1<<12;int c=0,ans,n,m,fl;int state[MAXSTATE];int p[13][MAXSTATE];int f[13];void kick() //去掉一行中有相邻1的状态{    for (int i=0;i<MAXSTATE;i++)        if ( ( i & (i<<1) ) == 0 ) state[c++]=i;}int main(){    cin >> n >> m;    for (int i=1;i<=n;i++)        for (int j=1;j<=m;j++)        {            cin >> fl;            f[i]+=fl<<(m-j);        }    kick();    p[0][0]=1;    for (int i=1;i<=n;i++) //枚举行    {        for (int s=0;s<c;s++) //枚举上一行的状态        {            if ( (state[s] | f[i] )!=f[i] ) continue; //判断土地是否可以种            for (int s1=0;s1<c;s1++) //枚举当前行的状态            {                if ( ((state[s1] | f[i-1] )==f[i-1]) && (state[s1] & state[s]) == 0 )//判断上一行的状态是否可以种在土地上,以及医这一行有没有相邻                    p[i][s]=(p[i][s]+p[i-1][s1])%100000000;//累加方案数            }        }    }    for (int s=0;s<c;s++)    {        ans=(ans+p[n][s])%100000000; //统计答案    }    cout << ans << endl;}

2.POJ2411

  给出一个n×m的棋盘,问用1×2的牌放满它(可以横放或竖放),共有有多少种方法。
  暴力方法就不说了,直接讲正解,一行的状态同样可以用一个二进制数保存,同样是当前行的状态只与上一行有关,但当前行的状态可以通过上一行的状态和dfs求出。
  但为了使全部填满,应该遵循以下规则:
第一种情况:上一行状态为0
  当上一行状态为0时,上下放一个竖的,只有这样才能保证在跳到i+1行时上面都是满的。
第二种情况:上一行状态为1
  当上一行状态为1时,有两种选择,第一种是不放(留给右边横放或下一行竖放),第二种是与左边一格一起横放一个(前提是左边的是空的)。
代码:

#include <iostream>#include <fstream>#include <string.h>using namespace std;const int MAXSTATE = 1<<11;int n,m;long long p[13][MAXSTATE];void dfs(int s,int s1,int i,int k)//搜索当前行的状态{    if (k>=m) //搜索完成    {        p[i][s]+=p[i-1][s1];        return ;    }    if ( (s1&(1<<k))!=0 )     {        dfs(s,s1,i,k+1);        if ( k!=0 &&  ( s & (1<<(k-1)) ) == 0 ) dfs( (s|(1<<k)|(1<<(k-1))) , s1 , i , k+1 );    }    else dfs( ( s | (1<<k) ) , s1 , i , k+1 );}int main(){    cin >> n >> m;    while (n!=0 && m!=0)    {        memset(p,0,sizeof p);        p[0][MAXSTATE-1]=1;        dfs(0,MAXSTATE-1,1,0);        for (int i=2;i<=n;i++)        {            for (int s1=0;s1<MAXSTATE;s1++)            {                if (p[i-1][s1]==0) continue;                dfs(0,s1,i,0);    //          cerr << p[i-1][s1];            }        }        cout << p[n][(1<<m)-1] << endl;        cin >> n >> m;    }}
1 0
原创粉丝点击