开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 31 天,点击查看活动详情
今天继续进行状态压缩DP的学习。
题目描述
农夫约翰的土地由 个小方格组成,现在他要在土地里种植玉米。
非常遗憾,部分土地是不育的,无法种植。
而且,相邻的土地不能同时种植玉米,也就是说种植玉米的所有方格之间都不会有公共边缘。
现在给定土地的大小,请你求出共有多少种种植方法。
土地上什么都不种也算一种方法。
输入格式
第 11 行包含两个整数 和 。
第 行:每行包含 个整数 或 ,用来描述整个土地的状况, 表示该块土地肥沃, 表示该块土地不育。
输出格式
输出总种植方法对 取模后的值。
数据范围
输入样例:
2 3
1 1 1
0 1 0
输出样例:
9
题目分析
本题首先依旧按照原本的思想进行思考。
定义 f[i][j] 表示第 i 行状态为 j 且前 i-1 行已经排布完成的方案数。
状态依旧由二进制排列表示,限制为上下左右无相邻。
本题的限制为某些位置不能放置 1,即进一步约束了合法状态的数目。
我们按行从小到大遍历,将每一行不能放置的位置设为 1,其余位置设为 0,从而得到一个二进制排列状态,将其作为一种方案限制最后的答案,具体写法代码展示。
要注意在进行最初的数据读入时行的范围为 1~n,列的范围为 0~m-1,因为我们要将当前行作为一种方案,而二进制状态的第一位为 0 位。
Accept代码
#include <bits/stdc++.h>
using namespace std;
const int N = 15, M = 1 << 12, mod = 1e8;
int n, m;
int w[N];
int f[N][M];
vector<int> sta, head[M];
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i ++)
for (int j = 0; j < m; j ++) // 此处必须为 j from 0 to m - 1
{
int t; cin >> t;
w[i] += !t * (1 << j);
}
// for (int i = 1; i <= n; i ++) cout << w[i] << "\n";
for (int i = 0; i < 1 << m; i ++)
if (!(i & i << 1)) sta.push_back(i);
for (int i = 0; i < sta.size(); i ++)
for (int j = 0; j < sta.size(); j ++)
{
int a = sta[i], b = sta[j];
if (!(a & b)) head[a].push_back(b);
}
f[0][0] = 1;
for (int i = 1; i <= n + 1; i ++)
for (int j = 0; j < sta.size(); j ++)
{
int a = sta[j];
if (!(a & w[i])) for (auto b : head[a])
f[i][a] = (f[i][a] + f[i - 1][b]) % mod;
}
cout << f[n + 1][0];
return 0;
}