• 洛谷 P1924 poj 1038


    Description:

      给你一个n * m的方格纸,有一些格子无法被覆盖,然后用2*3的格子覆盖这个方格纸,问你最多能放多少个格子

    神级状压

    为了弄清楚这道题翻了无数篇解题报告,最后终于搞明白了

    用三进制表示每行的状态。

    比如对于第i行第j列的格子,如果i-1行,i行的j列都是空的则用0表示,i行的j列不能放用2表示,剩下的(仅i - 1行的j列不能放)用1表示

    然后深搜进行转移

    干讲没意思,具体看注释,写的很清楚了(AC代码是poj的,稍微改一下输入输出就是洛谷的、

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    using namespace std;
    const int N = 59050;
    /*pre表示你枚举的上一个状态,now表示你现在枚举的状态*/
    int dp[2][N], mp[160][15], pre[12], now[12], n, m, d, cur;
    int base[] = {0,1,3,9,27,81,243,729,2187,6561,19683,59049};
    void init(){
        memset(mp, 0, sizeof(mp));
        memset(dp, -1, sizeof(dp));
    }
    /*3进制转10进制*/
    int trans(int *t){
        int ans = 0;
        for(int i = 1; i <= m; i++)
          ans += t[i] * base[i];
        return ans;
    }
    /*10进制转3进制*/
    void chan(int t, int *p){
        for(int i = 1; i <= m; i++)
          p[i] = t % 3, t /= 3;
        return ;
    }
    /*i为当前行,j为当前状态,cnt为格子数目,tmp为当前压缩后整行状态*/
    void dfs(int i, int j, int cnt, int tmp){
        int k;
        dp[i & 1][tmp] = max(dp[i & 1][tmp], cnt);
        if(j >= m) return ;
        /*放上3行2列的格子
          如果该层状态和前一层的状态都允许 也就是都能放格子 那么就放上格子继续搜下去*/
        if(!pre[j] && !pre[j + 1] && !now[j] && !now[j + 1]){
            now[j] = now[j + 1] = 2;  //放格子之后状态就都是2了
            k = trans(now); dfs(i, j + 2, cnt + 1, k);//因为两格都被覆盖,跳过去搜
            now[j] = now[j + 1] = 0;  //回溯
        }
        /*放上2行3列的格子
         这个就只跟你当前的状态 也就是i行j列和i-1行j列是否为空 而这一信息仅存储在now[j]上 j+1列 j+2列同理*/
        if(j < m - 1 && !now[j] && !now[j + 1] && !now[j + 2]){
            now[j] = now[j + 1] = now[j + 2] = 2; //同样修改 搜下去 回溯
            k = trans(now);  dfs(i, j + 3, cnt + 1, k);
            now[j] = now[j + 1] = now[j + 2] = 0;
        }
        dfs(i, j + 1, cnt, tmp);
        return ;
    }
    int main(){
        int T, x, y;
        scanf("%d", &T);
        while(T--){
            init();
            scanf("%d%d%d", &n, &m, &d);
            for(int i = 1; i <= d; i++){
                scanf("%d%d", &x, &y);
                mp[x][y] = 1;
            }
            for(int i = 1; i <= m; i++)
              pre[i] = 2;
            int tmp = trans(pre);
            dp[0][tmp] = 0;  //对于第0行来说,只有啥都不能放这一种状态 将该状态价值为0
            for(int i = 1; i <= n; i++){
                memset(dp[i & 1], -1, sizeof(dp[i & 1]));  //初始化本次要用的dp数组
                for(int j = 0; j < base[m + 1]; j++){  //枚举上一行的状态
                    /* 如果上一行的状态dp数组为-1,说明上一层的该状态根本达不到,那么就不能用来转移*/
                    if(dp[i + 1 & 1][j] == -1) continue;
                    chan(j, pre); //将上一层的状态转换成3进制
                    for(int k = 1; k <= m; k++) 
                      if(mp[i][k]) now[k] = 2; //如果此时第k位无法覆盖,那么就为状态为2
                    /*不然,上层2状态变为1状态,其余为0(根据状态的定义就可以得出)*/
                      else now[k] = max(pre[k] - 1, 0); 
                    cur = dp[i + 1 & 1][j];  //cur表示上一层该状态达到的最大价值
                    dfs(i, 1, cur, trans(now)); //dfs转移 因为第一格前为第0格不能放,所以状态为1
                }
            }
            int ans = 0;
            for(int i = 0; i < base[m + 1]; i++)
              ans = max(ans, dp[n & 1][i]);
            printf("%d
    ", ans);
        }
        return 0;
    }

    用这种多进制表示然后dfs转移的方法可以做出绝大多数的状压题,尤其是状态表示较复杂(一般需要表示两个及以上状态的),转移方程不好写而冗余状态较多的题

    比如炮兵阵地,也可以用三进制表示状态然后dfs转移。但是由于炮兵阵地的总状态数并不多,所以可以直接枚举子集,写起来更省力。

  • 相关阅读:
    form表单提交target属性使用
    window.showModalDialog
    mybaits中date类型显示时分秒(orcle数据库)
    mybatis中in查询
    偷懒的inline-block解决方法
    10. python单元测试(一)
    9. Request & 爬虫
    8. 类与对象
    7. python异常处理&异常基类学习
    6. IO及文件操作
  • 原文地址:https://www.cnblogs.com/Rorshach/p/9279058.html
Copyright © 2020-2023  润新知