• poj3245_状压dp


    http://poj.org/problem?id=3254

    题意:给出一个n行m列的草地,1表示肥沃,0表示贫瘠,现在要把一些牛放在肥沃的草地上,但是要求所有牛不能相邻,问你有多少种放法。

    分析:假如我们知道第 i-1 行的所有的可以放的情况,那么对于第 i 行的可以放的一种情况,我们只要判断它和 i - 1 行的所有情况的能不能满足题目的所有牛不相邻,如果有种中满足,那么对于 i 行的这一中情况有 x 中放法。

    前面分析可知满足子状态,我们我们确定可以用dp来解决。

    但是我们又发现,状态是一种放法,不是我们平常dp的简单的状态,所以要用状态压缩!

    但是什么是状态压缩呢?

    比如前面的情况,一种放法是最多由12个 0 或者 1 组成的,那么我们很容易想到用二进制,用二进制的一个数来表示一种放法。

    定义状态dp【i】【j】,第 i 行状态为 j 的时候放牛的种数。j 的话我们转化成二进制,从低位到高位依次 1 表示放牛0表示没有放牛,就可以表示一行所有的情况。

    那么转移方程 dp【i】【j】=sum(dp【i-1】【k】)

    状态压缩dp关键是处理好位运算。

    这个题目用到了 & 这个运算符。

    用 x & (x<<1)来判断一个数相邻两位是不是同时为1,假如同时为 1 则返回一个值,否则返回 0 ,这样就能优化掉一些状态

    用 x & y 的布尔值来判断相同为是不是同时为1。

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 using namespace std;
     5 typedef long long LL;
     6 const int mod = 1e8;
     7 const int N = 13;
     8 const int M = 1 << N;
     9 int dp[N][M], map[N], res[M];
    10 int judge1(int x)
    11 {
    12     return (x & (x << 1));
    13 }
    14 int judge2(int i, int j)
    15 {
    16     return (map[i] & res[j]);
    17 } 
    18 int main()
    19 {
    20     int n, m;
    21     scanf("%d %d", &n, &m);
    22     int temp;
    23     for(int i = 1; i <= n; i++)
    24         for(int j = 1; j <= m; j++)
    25         {
    26             scanf("%d", &temp);
    27             if(temp == 0)
    28                 map[i] += (1 << (j - 1));//注意不肥沃为1,这里很巧妙
    29         }
    30     int k = 0;
    31     for(int i = 0; i < (1 << m); i++)
    32         if(judge1(i) == 0)
    33             res[k++] = i;
    34     for(int i = 0; i < k; i++)
    35     {
    36         if(!judge2(1, i))//如果在不肥沃的地方放牛(相与为1时),就不保存
    37             dp[1][i] = 1;
    38     }
    39     for(int i = 2; i <= n; i++)
    40     {
    41         for(int j = 0; j < k; j++)
    42         {
    43             if(judge2(i, j))
    44                 continue;
    45             for(int f = 0; f < k; f++)
    46             {
    47                 if(judge2(i - 1, f))
    48                     continue;
    49                 if(!(res[j] & res[f]))
    50                     dp[i][j] += dp[i - 1][f];//dp[i][0]保存了前面几行的所有状态
    51             }
    52         }
    53     }
    54     LL sum = 0;
    55     for(int j = 0; j < k; j++){
    56         sum = (sum + dp[n][j]) % mod;
    57     }
    58     printf("%lld
    ", sum);
    59     return 0;
    60 }
  • 相关阅读:
    向存储过程中传入列表参数
    TRUNCATE TABLE (Transact-SQL)
    Program.cs
    联想G40-30 安装win10系统
    完美解决github访问速度慢[转]
    关于Visual studio 2017安装方法的若干问题
    [转载] 中国象棋软件-引擎实现(七)测试程序
    [转载] 中国象棋软件-引擎实现(六)局面评估
    [转载] 中国象棋软件-引擎实现(五)历史启发及着法排序
    [转载] 中国象棋软件-引擎实现(四)搜索算法
  • 原文地址:https://www.cnblogs.com/luomi/p/5659895.html
Copyright © 2020-2023  润新知