链接https://www.luogu.com.cn/problem/P1879
题目
农场主John新买了一块长方形的新牧场,这块牧场被划分成M行N列(1 ≤ M ≤ 12; 1 ≤ N ≤ 12),每一格都是一块正方形的土地。John打算在牧场上的某几格里种上美味的草,供他的奶牛们享用。
遗憾的是,有些土地相当贫瘠,不能用来种草。并且,奶牛们喜欢独占一块草地的感觉,于是John不会选择两块相邻的土地,也就是说,没有哪两块草地有公共边。
John想知道,如果不考虑草地的总块数,那么,一共有多少种种植方案可供他选择?(当然,把新牧场完全荒废也是一种方案)
做法分析
这道题乍一看就知道是一道状态压缩dp,而且和之前写过的一个国王游戏的题非常之类似。
可以得到状态dp[i][s] 其中i为行数,s为当前状态。从而得到状态转移方程
dp[i][s]+=dp[i-1][s1]; 其中s1是上一行的状态。
每一行的所有可行状态通过预处理得到。
通过上一行的状态的可行数推导出下一行的满足要求的状态的可行数
最后把最后一行的所有状态的可行数相加就得到了答案。详细介绍都在代码的注释里。
代码
#include<bits/stdc++.h> using namespace std; long long dp[15][1000005]; //转移数组 vector<long long>g[15]; //存每一行的可行状态 long long a[15][15]; //存图 long long b[15]; //存放每一行的状态二进制表示 int main(){ int n,m; cin>>n>>m; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ cin>>a[i][j]; b[i]=b[i]<<1; //读图 b[i]+=a[i][j]; //将草地状态放在数组里 } } long long dks=(1<<m)-1; //得到最大的一个状态 for(long long i=1;i<=n;i++){ //预处理算出每一行单独来看的可行状态并放在动态数组里面 for(long long j=0;j<=dks;j++){ if(j&(j<<1))continue; if((b[i]|j)==b[i]){ g[i].push_back(j); } } } dp[0][0]=1; //别忘了初始化 g[0].push_back(0); for(int i=1;i<=n;i++){ //核心代码 遍历所有行 遍历上一行的所有状态 遍历本行的所有状态 for(auto s1:g[i-1]) for(auto s:g[i]){ if(s&s1)continue; //状态不兼容 略过 dp[i][s]+=dp[i-1][s1]; //转移方程 } } long long ans=0; for(long long j=0;j<=dks;j++){ ans+=dp[n][j]; ans%=100000000; } cout<<ans<<endl; }