欲望以提升热忱,毅力以磨平高山!|

XichenOC

园龄:1个月粉丝:4关注:0

2025-02-07 15:50阅读: 5评论: 0推荐: 1

P1879 [USACO06NOV] Corn Fields G

P1879 [USACO06NOV] Corn Fields G

题目翻译:

给定一个 \(n \times m\) 的矩阵,其中有些地方可以放点,有些则不可以,且任意两点不能相邻,即任意一点上下左右都不能有其它点。求有多少种放点的方案。

思路:

发现行列的长度较小,所以考虑状压 \(dp\)考虑限制:

  • 发现对于场地本地的限制可已提前维护好状态,在二进制下若为一就是不可以放置,为零就是可以。这样后面就可以直接相与是否等于零,来判断是否合法
  • 对于左右不能相邻,也可以提前预处理出来,将状态左右移在判断是否重合,即与原本相与是否等于零,最后将所有合法的给储存到 \(ok[i]\) 即可
  • 对于上下不能相邻,就枚举两个状态。看是否相同即可。

实现:

\(dp[i][j]\) 表示第 \(i\) 行状态为 \(j\) 的方案数,这样枚举当前行数 \(i\),上一行的状态 \(ok[j]\) 和下一行状态 \(ok[k]\),看是否合法在转移。转移方程为 \(dp[i][ok[j]]+=dp[i-1][ok[k]]\)

完整代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=5000;
const int P=100000000;
int ok[N];
int dp[15][N];
int e[15];
signed main(){
    int m,n;
    scanf("%lld%lld",&m,&n);
    for(int i=1;i<=m;i++){
        for(int j=1;j<=n;j++){
            int op;
            scanf("%lld",&op);
            e[i]+=op==1?0:1<<(n-j);
        }
    }
    int cnt=0;
    for(int i=0;i<(1<<n);i++){
        if(!(((i<<1)|(i>>1))&i)){
            ok[++cnt]=i;
        }
    }
    dp[0][0]=1;
    for(int i=1;i<=m;i++){
        for(int j=1;j<=cnt;j++){
            if(!(ok[j]&e[i])){
                for(int k=1;k<=cnt;k++){
                    if(!(ok[k]&e[i-1]) && !(ok[k]&ok[j])){
                        dp[i][ok[j]]+=dp[i-1][ok[k]];
                        dp[i][ok[j]]%=P;
                    }
                }
            }
        }
    }
    int ans=0;
    for(int i=1;i<=cnt;i++){
        ans+=dp[m][ok[i]];
        ans%=P;
    }
    printf("%lld\n",ans);
}

状压 \(dp\) 讲解

本文作者:XichenOC

本文链接:https://www.cnblogs.com/XichenOC/p/18702741

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   XichenOC  阅读(5)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起