[USACO06NOV] Corn Fields G 题解

题目链接

[USACO06NOV] Corn Fields G

题解

这是一道典型的状压dp,对于 \(M\)\(N\) 列的图,由于每个点只有 \(1\)\(0\) 两种状态,我们将其压缩为一个长度为 \(M\) 的数组,数组( \(g\) )的每一项 \(g_{i}\) 表示状态的二进制表示法表示的数(如 \(101\) 表示为 \(5\) ),我们预先处理 \(cnt\) 个可能的状态,将其存放在数组 \(sta\) 当中,我们在用 \(f_{i,j}\) 来表示表示第 \(i\) 行选择状态 \(j\) 的最大情况数
易得到状态转移方程式:
\( f_{i,j}= \begin{cases} f_{i,j} \\f_{i,j}+f_{i-1,k} \end{cases} \)
其中 \(k\) 表示第 \(i-1\) 行的状态为 \(sta_{k}\) ,那么对于状态 \(j\),它存在的条件是:

  • 状态 \(j\) 必须可以存在于第 \(i\) 行,即不能有原图上标记为 \(0\) 的点而状态 \(j\) 上标记为 \(1\) 的点,判断代码:
if (sta[i]&(~g[1]));
  • 状态 \(j\) 不能与第 \(i-1\) 行的状态 \(k\) 冲突,即状态 \(k\) 上为 \(1\) 的点,状态 \(j\) 上的这个点不能为 \(1\) ,判断代码:
if (sta[k]&sta[j]);
  • 同时注意一个细节,在遍历 \(k\) 时,\(k\) 也要符合第 \(i-1\) 行,即:
if (sta[k]&(~g[i-1]));

对于第一行,要单独处理,因为不存在第 \(i-1\) 行。对于最后输出的答案,要把最后一行所有可能的状态的情况数相加才行

int ans=0;
for (int i=1;i<=cnt;i++) ans+=dp[m][i];

CODE

#include<cstdio>
using namespace std;
#define P 100000000

int m,n;
int g[13];
int sta[5000];      //预处理的所有可能的状态
int dp[13][5000];   //dp[i][j]表示第i行如果选择状态j,最大情况数
int cnt;

void dfs(int s,int t){
    if (t>=n){
        sta[++cnt]=s;
        return;
    }
    dfs(s,t+1);    //当第t+1个点不选
    dfs(s+(1<<t),t+2);    //当第t+1个点选,直接跳转至t+2对t+2+1的点进行选择
}

int main(){
    scanf("%d%d",&m,&n);
    for (int i=1;i<=m;i++){
        for (int j=1;j<=n;j++){
            int tmp;
            scanf("%d",&tmp);
            g[i]=g[i]+(tmp<<(n-j));  //处理每一行的状态(转换为二进制值)
        }
    }
    dfs(0,0);   //预处理
    for (int i=1;i<=cnt;i++){
        if (sta[i]&(~g[1])) continue;   //判断状态i对于第一行是否可选
        else dp[1][i]++;                //如果可选,情况+1
    }//第一行特殊处理
    for (int i=2;i<=m;i++){
        for (int j=1;j<=cnt;j++){
            for (int k=1;k<=cnt;k++){
                if (sta[j]&(~g[i])) continue;   //判断状态i对于第i行是否可选
                if (sta[k]&(~g[i-1])) continue; //判断状态k对于第i-1行是否可选
                if (sta[k]&sta[j]) continue;    //判断状态k和状态j是否可以作为相邻的两行
                dp[i][j]+=dp[i-1][k];           //满足上述三个条件后,dp[i][j]加上dp[i-1][k]
            }
        }
    }//第二行之后的处理
    int ans=0;
    for (int i=1;i<=cnt;i++) ans+=dp[m][i];  //对最后一行所有可能装状态的情况数相加,即为最后的答案
    printf("%d",ans%P);
    return 0;
}
posted @ 2024-11-12 21:40  ZYStream  阅读(15)  评论(0编辑  收藏  举报