状态压缩优化动归

  这个星期的任务就是它了,看了看周伟的论文,开始做题,做一道传一道吧。

【POJ 3254】Corn Fields

【题目大意】给一个矩阵,可以放牧的点为1,不可以的为0,且放牧的时候任意2头牛不能相邻(包括上下和左右),问总共多少放牧方案

【解析】状态转移方程很好列,用f[i][j]表示第i行的状态为j的时候的方案数,j的二进制每一位表示一个格子0为不放,1为放。

很明显的有f[i][j]=∑f[i-1][k]  k为与j不冲突的放法。 边界是f[1][j]=0(j合理)/1(j不合理)

刚学位运算,算是用到了一些技巧:

①判断是否相邻, 可以用k&(k<<1)

②存初始矩阵的时候可以反过来用1表示不可以,0表示可以,那么判断放法时候合理的时候就简单多了,见代码里的check2()

POJ 3245
 1 /**
 2 *For 状态压缩dp练习
 3 *Prob    : POJ 3254
 4 *Data    : 2012-6-14
 5 *Author : ZhouHang
 6 */
 7 
 8 #include <cstdio>
 9 #include <cstring>
10 
11 #define MaxM 20
12 #define MaxState 5000
13 #define oo 100000000
14 
15 using namespace std;
16 
17 int tot=0,m,n;
18 int state[MaxState],s[MaxM];
19 int f[MaxM][MaxState];
20 
21 bool check(int k)
22 {
23     //验证相邻
24     if ( k&(k<<1) ) return false;
25     return true;
26 }
27 bool check2(int x,int k)
28 {
29     //s[k]中1是不能放牧
30     if (x&s[k]) return false;
31     else return true;
32 }
33 
34 //f[i][j] 第i行为j的方法数
35 void Dp()
36 {
37     memset(f,0,sizeof(f));
38     
39     //预处理第一行
40     for (int i=1; i<=tot; i++)
41         if ( check2(state[i],1) )
42             f[1][state[i]]=1;
43 
44     for (int i=2; i<=m; i++)
45      for (int j=1; j<=tot; j++)
46      {
47         if (!check2(state[j],i)) continue;
48         for (int k=1; k<=tot; k++)
49         {
50             //if (!check2(state[k],i-1)) continue;
51             if (state[k]&state[j]) continue;
52             f[i][state[j]]+=(f[i-1][state[k]]%oo);
53             f[i][state[j]]%=oo;
54         }
55      }
56 }
57 
58 int main()
59 {
60     
61     scanf("%d%d",&m,&n);
62     
63     int tmp;
64     for (int i=1; i<=m; i++)
65     {
66      s[i]=0;
67      for (int j=1; j<=n; j++)
68      {
69         scanf("%d",&tmp);
70         //反过来存,方便check2判断
71         if (tmp==0) s[i]+=1<<(n-j);
72      }
73     }
74 
75     //Search all states
76     int limit=(1<<n)-1;
77     for (int i=0; i<=limit; i++)
78      if ( check(i) ) state[++tot]=i;
79     
80     Dp();
81      
82     int ans=0;
83     for(int i=1; i<=tot; i++)
84         ans=(ans+f[m][state[i]])%oo;
85 
86     printf("%d\n",ans);
87 
88 
89     return 0;
90 }

 【POJ 1185】炮兵阵地

【题目大意】见原题

【解析】跟上面的那个类似,只不过这次每个状态要影响的就不是一行了,而是跟上行和上上行有关,于是我们的状态就要加一维了,用f[i][j][k]表示第i行状态下标是i,第j状态是k,原因是我们每次要枚举3行的状态,为了转移,必须记录两行以保证唯一,那么f[i][j][k]=max(f[i-1][k][t]+num[j])  num放的是每种状态的1的个数,因为空间只有64MB,所以我们要用到滚动数组

同样记录下位运算的技巧:可以用x&=(x-1)来去掉最靠后的一个1

POJ 1185
/**
*For 状态压缩dp练习
*Prob    : POJ 1185
*Data    : 2012-6-14
*Author : ZhouHang
*/

#include <cstdio>
#include <cstring>
#include <algorithm>

#define MaxState 610
#define MaxN 110

using namespace std;

int tot=0,m,n,ans=0;
int state[MaxState],s[MaxN],num[MaxState];
int f[MaxState][MaxState];
int tmp[MaxState][MaxState];

bool check(int k)
{
    //验证相邻
    if ( k&(k<<1) ) return false;
    if ( k&(k<<2) ) return false;
    return true;
}
bool check2(int x,int k)
{
    //s[k]中1是不能放
    if (x&s[k]) return false;
    else return true;
}
int count(int x)
{
    int cnt=0;
    while (x)
    {
        cnt++;
        x&=(x-1);//去掉最后一个1
    }
    return cnt;
}

void Dp()
{
    memset(tmp,-1,sizeof(tmp));

    //预处理第一行
    for (int j=1; j<=tot; j++)
      if ( check2(state[j],1) )
      {
            tmp[j][1]=num[j];
            if (tmp[j][1]>ans) ans=tmp[j][1];
      }

    for (int i=2; i<=m; i++)
    {
     for (int j=1; j<=tot; j++)  //当前行
     {
        if (!check2(state[j],i)) continue;
        for (int k=1; k<=tot; k++) //上一行
        {
            if ( state[k]&state[j] ) continue;
            for (int t=1; t<=tot; t++)
            {
                if (state[t]&state[j]) continue;
                if (tmp[k][t]==-1) continue; 
                f[j][k]=max(f[j][k],tmp[k][t]+num[j]);
                if (f[j][k]>ans) ans=f[j][k];
            }
        }
     }
     memcpy(tmp,f,sizeof(f));
    }
}

int main()
{
    freopen("dp.in","r",stdin);
    freopen("dp.out","w",stdout);
    
    scanf("%d%d",&m,&n);
    
    char tmp;
    for (int i=1; i<=m; i++)
    {
        s[i]=0;
        scanf("%*c");
        for (int j=1; j<=n; j++)
        {
            scanf("%c",&tmp);
            if (tmp=='H')
                s[i]+=1<<(n-j);
        }
    }

    //Search all states
    int limit=(1<<n)-1;
    for (int i=0; i<=limit; i++)
     if ( check(i) )
     {
        state[++tot]=i;
        num[tot]=count(i);
     }
    
    Dp();

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


    fclose(stdin); fclose(stdout);
    return 0;
}

 

posted @ 2012-06-14 11:49  守護N1身边  阅读(252)  评论(0编辑  收藏  举报