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 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步