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;
    }
  • 相关阅读:
    POJ 1659 Frogs' Neighborhood
    zoj 2913 Bus Pass(BFS)
    ZOJ 1008 Gnome Tetravex(DFS)
    POJ 1562 Oil Deposits (DFS)
    zoj 2165 Red and Black (DFs)poj 1979
    hdu 3954 Level up
    sgu 249 Matrix
    hdu 4417 Super Mario
    SPOJ (BNUOJ) LCM Sum
    hdu 2665 Kth number 划分树
  • 原文地址:https://www.cnblogs.com/2014nhc/p/6441888.html
Copyright © 2011-2022 走看看