玉米田
玉米田
农夫约翰的土地由 个小方格组成,现在他要在土地里种植玉米。
非常遗憾,部分土地是不育的,无法种植。
而且,相邻的土地不能同时种植玉米,也就是说种植玉米的所有方格之间都不会有公共边缘。
现在给定土地的大小,请你求出共有多少种种植方法。
土地上什么都不种也算一种方法。
输入格式
第 行包含两个整数 和 。
第 行:每行包含 个整数 或 ,用来描述整个土地的状况, 表示该块土地肥沃, 表示该块土地不育。
输出格式
输出总种植方法对 取模后的值。
数据范围
输入样例:
2 3 1 1 1 0 1 0
输出样例:
9
解题思路
状态压缩dp。当摆放了绿色的格子后,绿色格子上下左右四个位置(红色格子)都不可以再摆放物品。可以发现当要在第行摆放物品时,只会受到第行的影响,而往上的行不会影响到第行的摆放。因此状态定义时只用记录一行的状态就可以了。
定义状态表示所有摆完前行,且第行的状态为的所有摆放方案的集合。根据前一行的状态来进行划分。
首先的二进制中为的位在当前行是可以摆放的。
然后状态要满足,二进制下的不包含连续的两个,状态同理。
最后和在同一列上(同一数位上)不能同时为,即要满足。
因此状态转移方程为
AC代码如下,时间复杂度为:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int N = 15, M = 1 << 12, mod = 1e8; 5 6 int n, m; 7 int f[N][M]; 8 int g[N]; 9 10 bool check(int x) { 11 for (int i = 1; i < m; i++) { 12 if (x >> i & 1 && x >> i - 1 & 1) return false; 13 } 14 return true; 15 } 16 17 int main() { 18 scanf("%d %d", &n, &m); 19 for (int i = 1; i <= n; i++) { 20 int t = 0; // 把第i行的可种植的状态用二进制表示 21 for (int j = 0; j < m; j++) { 22 int v; 23 scanf("%d", &v); 24 t |= !v << j; // 可种植为0,不可种植为1,因此当j&t==0时j的状态才合法 25 } 26 g[i] = t; 27 } 28 29 f[0][0] = 1; // 第0行摆放物品 30 for (int i = 1; i <= n + 1; i++) { 31 for (int j = 0; j < 1 << m; j++) { 32 if (!(j & g[i]) && check(j)) { // j是合法种植方案,且j不存在连续相邻的两个1 33 for (int k = 0; k < 1 << m; k++) { 34 // 这里可以省略k不存在连续相邻的两个1这个条件是因为,当k不合法时一定有f[i-1][k] = 0 35 if (!(j & k)) f[i][j] = (f[i][j] + f[i - 1][k]) % mod; 36 } 37 } 38 } 39 } 40 41 printf("%d", f[n + 1][0]); // 摆完前n行,此时第n+1行一定没有任何物品 42 43 return 0; 44 }
可以先把合法的状态预处理出来,AC代码如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int N = 15, M = 1 << 12, mod = 1e8; 5 6 int n, m; 7 int f[N][M]; 8 vector<int> state, st[M]; 9 10 bool check(int x) { 11 for (int i = 1; i < m; i++) { 12 if (x >> i & 1 && x >> i - 1 & 1) return false; 13 } 14 return true; 15 } 16 17 int main() { 18 scanf("%d %d", &n, &m); 19 20 for (int i = 0; i < 1 << m; i++) { // 预处理出来不包含连续两个1的状态 21 if (check(i)) state.push_back(i); 22 } 23 for (auto &i : state) { 24 for (auto &j : state) { 25 if ((i & j) == 0) st[i].push_back(j); // 表示状态i可以由j转移过来 26 } 27 } 28 29 f[0][0] = 1; 30 for (int i = 1; i <= n + 1; i++) { 31 int t = 0; 32 for (int j = 0; j < m; j++) { 33 int x; 34 scanf("%d", &x); 35 t |= !x << j; 36 } 37 38 for (auto &j : state) { 39 if ((j & t) == 0) { 40 for (auto &k : st[j]) { 41 f[i][j] = (f[i][j] + f[i - 1][k]) % mod; 42 } 43 } 44 } 45 } 46 47 cout << f[n + 1][0]; 48 49 return 0; 50 }
参考资料
AcWing 327. 玉米田(算法提高课):https://www.acwing.com/video/404/
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/16600387.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效