题目链接:
玉米田
题目大意:
思路分析:
- 状态表示f[i][S] 表示摆到第行时且第i行的状态为S时的方案,且S可以看成一位二进制数,0表示不放玉米,1表示放;
- 状态转移:这是一道棋盘类状压Dp,且发现第i行的摆放方案只与第i-1行相关,所以f[i][S] 可以由f[i-1][S’] 转移;
- 转移的条件:S,S’均为合法摆放方案,即没有相邻两个一, S&S’=0;
- 技巧1:使用预处理求出所有合法的摆放方法, 并且求出每个合法S可以被合法转移的S‘;
技巧2:可以将初始的不能摆放的地方标记为1,并且将第行的初始状态压缩为十进制数g[i],这样合法的S必须满足S&g[i]==0;
AC代码
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include<vector>
using namespace std;
const int N=14,M=1<<12,mod=1e8;
int f[N][M],g[N];
vector<int> h[M];
vector<int> state;
int n,m;
bool check(int x)
{
for(int i=0;i<m;i++){
if((x>>i&1)&&(x>>(i+1)&1))
return false;
}
return true;
}
//预处理
void pre()
{
for(int i=0;i<(1<<m);i++){
if(check(i))
state.push_back(i);
}
for(int i=0;i<state.size();i++){
for(int j=0;j<state.size();j++){
int a=state[i],b=state[j];
if(a&b) continue;
h[i].push_back(j);//方便后面的枚举
}
}
}
int main()
{
cin>>n>>m;
pre();
for(int i=1;i<=n;i++)
for(int j=0;j<m;j++){
int a;
scanf("%d",&a);
g[i]+=((!a)<<j);//技巧2
}
f[0][0]=1;
for(int i=1;i<=n+1;i++){
for(int j=0;j<state.size();j++){
if(g[i]&state[j]) continue;
for(int b:h[j])
//转移方程
f[i][j]=(f[i][j]+f[i-1][b])%mod;
}
}
cout<<f[n+1][0]<<endl;
return 0;
}