【AcWing】327.种玉米(状态压缩+DP)
描述
农夫约翰的土地由 M×N
个小方格组成,现在他要在土地里种植玉米。
非常遗憾,部分土地是不育的,无法种植。
而且,相邻的土地不能同时种植玉米,也就是说种植玉米的所有方格之间都不会有公共边缘。
现在给定土地的大小,请你求出共有多少种种植方法。
土地上什么都不种也算一种方法。
输入格式
第 1
行包含两个整数 M 和 N
。
第 2…M+1
行:每行包含 N 个整数 0 或 1,用来描述整个土地的状况,1 表示该块土地肥沃,0
表示该块土地不育。
输出格式
输出总种植方法对 108
取模后的值。
数据范围
1≤M,N≤12
输入样例:
2 3
1 1 1
0 1 0
输出样例:
9
思路&感受
状态压缩曾经尝试入行ACM时知道有这么回事,最近刷力扣发现有一类题可以用此类方法处理,想着既然没有学习过此类题解法便来试试
预备知识
状态压缩基础:位运算
此处的位运算是指将两个十进制的数在二进制下进行的,比如3的二进制是11,2的二进制是10,则11&10=10=2
状态压缩的一些应用:
-
判断一个数字x二进制下第i位是不是等于1。
方法:if(((1<<(i−1))&x)>0)
将1左移i-1位,相当于制造了一个只有第i位上是1,其他位上都是0的二进制数。然后与x做与运算,如果结果>0,说明x第i位上是1,反之则是0。 -
将一个数字x二进制下第i位更改成1。
方法:x=x|(1<<(i−1))
证明方法与1类似,此处不再重复证明。 -
把一个数字二进制下最靠右的第一个1去掉。
方法:x=x&(x−1)
说实话移位这块之前上汇编时候就没太学好,着实是看了好久,,,
题解
题目意思很明确,玉米田中的玉米前后左右是不可以相邻的,这个背景和八皇后类似。但相比八皇后还加了限制:存在肥田和荒地
在此考虑将每一行抽象成一个01串(二进制),通过位运算来判断在某块田上是否可以进行玉米种植
对于动态规划问题,我们先确定状态转移方程:令
d
p
(
i
,
j
)
dp(i,j)
dp(i,j)表示在第
i
i
i地块下的状态(是否种植),
我们假设1表示可以种植,0不可以种植,现在有两相邻行地块如下:
S
1
=
1010
,
S
2
=
0101
S_1=1010,S_2=0101
S1=1010,S2=0101
这两行合法吗?合法,我们将两地块与上,得0。
我们再枚举其他不合法的地块可以看出,当
S
i
a
n
d
S
i
+
1
!
=
0
S_i and S_{i+1}!=0
SiandSi+1!=0时,是不合法的
针对不可以种植的土地,我们再令
g
(
i
)
=
=
1
g(i)==1
g(i)==1,此时只要有状态
s
a
n
d
g
(
i
)
=
=
0
sandg(i)==0
sandg(i)==0
感受
毕竟不是ACMer,第一次入门此类题目真的有点难。这篇笔记也是自己敲了很久才写出来的,很多东西还是要以后慢慢补
代码
#include<iostream>
#include<vector>
using namespace std;
const int N = 13, Mod = 100000000;
vector<int> status, head[1 << N];
int n, m, x, dp[14][1 << N], g[N];
bool check(int x) {
//有没有相邻的1
return !(x & x >> 1);//和移位进行与运算
}
inline void solve() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> x;
g[i] += (!x << (j - 1));//位运算的应用,表荒废的土地
}
}
for (int i = 0; i < (1 << m); i++) {
if (check(i))
status.push_back(i);//不存在种植左右相邻的玉米
}
for (int i = 0; i <= status.size(); i++) {
for (int j = 0; j < status.size(); j++) {
if (!(status[i] & status[j]))
head[i].push_back(j);//对应状态
}
}
dp[0][0] = 1;
for (int i = 1; i <= n + 1; i++) {
for (int a = 0; a < status.size(); a++) {
if (status[a] & g[i])
continue;
for (int b = 0; b < head[a].size(); b++) {
//状态转移
dp[i][a] = (dp[i][a] + dp[i - 1][head[a][b]]) % Mod;
}
}
}
cout << dp[n + 1][0] << endl;
}
int main() {
solve();
return 0;
}