poj2441  poj1185 状态压缩DP

解题报告

题目 poj1185   poj2411

通过做poj1185poj2411,初步学习了状态压缩Dp。重点还是如何设定状态和转移,只不过这个状态会用到维压缩的表示方法,这个做的原因就是这些问题的无后效性很难满足,因此我么你需要将这个问题中能够达到的状态都列举出来,其实回头想一想,01背包的解法就是我们对物品排列向体积状态的一个压缩。 状态压缩dp更加体现了记忆化搜索与dp的紧密联系。

 

Poj1182

需要先处理对于一行来说,如果只有要求任意两个相邻差为2,我们搜索出方案数。(即一行内,满足任意两个相邻差为2的方案个数及方案是什么),然后我们定义状态f[i][j][k]表示第i行状态是j,第i-1行状态是k时前i行的最多能放的个数。转移方程

F[i][j][k] = max{f[i – 1][k][l] + one_num[j]}   one_num[j]表示在状态为j时,1的个数) 这是一个思维的转移,由于方案数很少,可以接受。

AC  code

#include <cstdio>

#include <cstring>

#define MAXST 300

#define MAX(a, b) ((a > b) ? (a) : (b))

 

int esit[3000], state[MAXST], one_num[MAXST];

int f[2][MAXST][MAXST];

int able[110];

char str[110];

int n, m, stnum, SKS;

 

bool Can(int st, int i){

         if(st & (1 << i)) return false;

         if(i > 0 && (st & (1 << i - 1))) return false;

         if(i > 1 && (st & (1 << i - 2))) return false;

         if(i < m - 1 && (st & (1 << i + 1))) return false;

         if(i < m - 2 && (st & (1 << i + 2))) return false;

         return true;

}

 

int Get_one_num(int gt){

         int ans = 0;

         while(gt){

                   if(gt & 1) ans ++;

                   gt >>= 1;

         }

         return ans;

}

 

void BFS(){

         int front;

         front = stnum = 0;

         memset(esit, 0, sizeof(esit));

         state[stnum ++] = 0;

         esit[0] = 1;

         one_num[0] = 0;

         while(front < stnum){

                   int i;

                   int st = state[front ++], gt;

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

                            if(Can(st, i)) gt = st ^ (1 << i);

                            else continue;

                            if(!esit[gt]){

                                     esit[gt] = 1;

                                     state[stnum] = gt;

                                     one_num[stnum ++] = Get_one_num(gt);

                            }

                   }

         }

}

 

int Dp(){

         int i, j, k, l, max = 0;

         if(n == 1){

                   for(j = 0; j < stnum; j ++)

                            if((state[j] & able[0])) continue;

                            else max = MAX(max, one_num[j]);

                   return max;

         }

    SKS = 0;

         memset(f, 0, sizeof(f));

         for(k = 0; k < stnum; k ++)

                   for(j = 0; j < stnum; j ++)

                            if((state[k] & able[0]) || (state[j] & able[1]) || (state[j] & state[k])) f[SKS][j][k] = 0;

                            else f[SKS][j][k] = one_num[j] + one_num[k];

         for(i = 2; i < n; ++ i){

                   for(j = 0; j < stnum; ++ j)

                            for(k = 0; k < stnum; ++ k){

                                     if((state[j] & able[i]) || (state[k] & able[i - 1]) || (state[j] & state[k])) continue;

                                     for(l = 0; l < stnum; ++ l)

                                               if((state[l] & able[i - 2]) || (state[l] & state[j]) || (state[l] & state[k])) continue;

                                               else f[SKS ^ 1][j][k] = MAX(f[SKS ^ 1][j][k], f[SKS][k][l] + one_num[j]);

                            }

                   SKS ^= 1;

         }

         for(j = 0; j < stnum; j ++)

                   for(k = 0; k < stnum; k ++)

                            max = MAX(max, f[SKS][j][k]);

         return max;

}

 

int main(){

         int i, j, ans;

         while(~scanf("%d %d", &n, &m)){

                   memset(able, 0, sizeof(able));

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

                            scanf("%s", str);

                            for(j = m - 1; j >= 0; -- j){

                                     if(str[j] == 'H')

                                               able[i] |= 1 << (m - j - 1);

                            }

                   }

                   BFS();

                   ans = Dp();

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

         }

         return 0;

}

 

 

 

Poj2411;

这个题很明显要我们把两行连起来考虑,因为第i行竖着放的时候会影响到第一行,

状态转移 f[i][s1] = f[i – 1][s2] (s1s2 合起来后,必须s2饱和,s1可随意)

也就是说f[i][s1]表示的是当前i-1行全部覆盖,第i行状态为s1,第i-1行状态为s2时的方案数。横着放所覆盖两个格都为1,竖着放上面为零,下面为1.由于我们搜索时是在由s2推出s1,因此,i-1行是零的的位置表示没有被横着放覆盖住,需要我们在第i行用竖着放的方式去覆盖。因此我们需要规定:“竖着放上面为零,下面为1“,下面为1才会限制我们在第i行的操作。

 

AC code

 

// 在枚举第i - 1行的状态时,我们已经确定第i行于第i - 1行对应的关系,如:第i-1行是1011011

//那么这个意思是在36列(从左到右)第ii-1行是竖着放的。那么我们只需要去搜索第i12457行水平放置或不放置的方法总数。

#include <cstdio>

#include <cstring>

#include <algorithm>

using namespace std;

typedef __int64 I64;

 

I64 dp[20][2050];

int n, m;

 

void dfs(int i, int s, int p, I64 val){  //i:行号,s: 这一行的状态,p:列号, val:前i - 1行的方法总数

    if(p == m){ dp[i][s] += val; return;}

    dfs(i, s, p + 1, val);                 //i行第p列不放置

    if((p < m - 1) && !(s & (1 << p)) && !(s & (1 << (p + 1)))) dfs(i, s | (1 << p) | (1 << (p + 1)), p + 2, val);//i行的pp+1水平放置。

}

 

int main(){

    int i;

    while(~scanf("%d %d", &n, &m), n && m){

        if((n & 1) && (m & 1)){printf("0\n"); continue;}

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

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

        dfs(1, 0, 0, 1);

        int t = 1 << m, j;

        for(i = 2; i <= n; i ++)

            for(j = 0; j < t; j ++)                                       //枚举第i - 1行的状态

                if(dp[i - 1][j]) dfs(i, (~j)&(t - 1), 0, dp[i - 1][j]);   //i - 1行在状态j下可行,那么去考虑第i

        printf("%I64d\n", dp[n][t - 1]);

    }

    return 0;

}

 

 

还要多做题啊,感觉理解的很不到位

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值