zoukankan      html  css  js  c++  java
  • bzoj1725: [Usaco2006 Nov]Corn Fields牧场的安排(状态压缩DP)

    本来就是一个写不大来动态规划的人,结果现在又了解到还有种东西叫状态压缩dp,唉。。。

    找了一道例题来试试看:http://www.lydsy.com/JudgeOnline/problem.php?id=1725

    解:我们用f[i][j]来表示当前第i行,状态为j的情况下,(且之前的1~i-1的方案数已经确定了),前i行有多少种方案,那么动态转移方程其实很明显:

    f[i][j]=sum(f[i-1][k]);(当上一行为第k种状态且与当前枚举的第i种状态不会产生相邻的土地,就加上f[i-1][k]的方案数)

    方程好写,可是状态很难表达啊,我们总不能开一个12维的数组吧。。。

    所以我们就利用了它的表达要么是0,要么是1,的这个特点用二进制解决。

    首先我们来看样例数据,和如何预处理:

    输入:

    2 3
    1 1 1
    0 1 0
    输出:

    9
    scanf("%d%d",&m,&n);
      for (int i=1;i<=m;i++)
       for (int j=n;j>=1;j--)
       {
            scanf("%d",&now);
            if (now==1) a[i]=a[i]|(1<<(j-1));
       }

    这段代码就是把输入数据当成二进制并转换成十进制,如第一行储存为7,第二行储存为2。方便接下来的操作

    sum=(1<<n)-1;
       for (int j=0;j<=sum;j++)
        if (((a[1]|j)==a[1])&&((j&(j<<1))==0))  dp[1][j]++;

    哈这段代码就蛮重要的啦

    dp[i][j]之前就讲过了它的意思,那么j状态具体指什么呢??

    比如dp[2][6]就表示我们把第二行种成1 1 0这种样子能获取的方案数,将j转换成2进制,就形象的代表了一种种草的形态。

    如j为2是表示0 1 0这种状态。

    那么我们就先预处理第一行在各种种草状态下是否符合题目要求,枚举j这种状态,须符合以下两点才是合法的:

    1.这种 状态本身不能有两个相邻的1,比如说1 1 0就不可以因为这样把两块草场种在了一起。

    //具体实现方法,我们把原本状态与当前枚举状态取或,如果说a[i]的值变化了,说明它有哪块地方原本为0,不允许种,但是当前情况却种了,导致那块草场变为了1

    2.这种状态要符合开始的输入,比如说我们枚举第二行为1 0 0就是不可以的,因为它原本是0 1 0,第一位是不允许种草的,咱不能硬种啊。。。

    //将当前状态左移或者右移(这个随便你),再与没移之前的状态进行and,如果答案为0,说明它这个状态没有相邻的可种草场,否则一定会有哪一块是1.

    符合以上两点,方案数就可以加1;

    然后我们正式进行dp,枚举当前的行与状态(请保证它是合法的,和第一行的判断方法一样),并且枚举上一行的状态,两个状态and一下,如果合法就加上上一行这种情况下的方案。

    恩,大概就是这样,发觉自己讲的好乱,,,,

    那下面是程序:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    int m,n,now,sum;
    int a[100],dp[20][10000];
    int main()
    {
      scanf("%d%d",&m,&n);
      for (int i=1;i<=m;i++)
       for (int j=n;j>=1;j--)
       {
            scanf("%d",&now);
            if (now==1) a[i]=a[i]|(1<<(j-1));
       }
      sum=(1<<n)-1;
       for (int j=0;j<=sum;j++)
        if (((a[1]|j)==a[1])&&((j&(j<<1))==0))  dp[1][j]++;
      for (int i=2;i<=m;i++)
       for (int j=0;j<=sum;j++)
       if (((a[i]|j)==a[i])&&((j&(j<<1))==0))
       {
            for (int k=0;k<=sum;k++)
            if ((j&k)==0) dp[i][j]=(dp[i][j]+dp[i-1][k])%100000000;
       }
       int ans=0;
       for (int j=0;j<=sum;j++) ans=(ans+dp[m][j])%100000000;
       cout<<ans<<endl;
       return 0;
    }
  • 相关阅读:
    自考 exec 7 3
    画函数图像
    Exec68
    applet main共存 五角星和五面形
    jcheckbox 用例
    JList 单击 ,双击例
    web性能优化(一)弱请求处理
    web性能优化(二)优化思维图
    算法之道左右旋转字符串
    腾讯微信面试题实现时间复杂度为O(1)的栈 20130226
  • 原文地址:https://www.cnblogs.com/2014nhc/p/6441888.html
Copyright © 2011-2022 走看看