1 条题解
-
0
牧场种植方案数问题题解
一、题目分析
农夫约翰需在M×N牧场中选择肥沃地块种植玉米,要求:
- 贫瘠地块(0)不可种植;
- 种植地块不能相邻(上下左右相邻);
- 计算所有可能的种植方案数(含不种植),结果对10⁸取模。
二、算法思路
采用状态压缩动态规划求解:
- 状态表示:用二进制数表示每行种植方案(如N=3时,101表示种植第1、3块地)。
- 合法状态筛选:通过
ok()
函数筛选无相邻1的状态(如110因相邻1非法)。 - 行内兼容检查:通过
fit()
函数排除在贫瘠地块种植的状态。 - 行间兼容检查:相邻行状态不能有上下相邻种植(即二进制位不能同时为1)。
三、代码实现
#include <cstdio> #include <cstring> using namespace std; #define mod 100000000 int M,N,top = 0; // top表示每行最多的状态数 int state[600],num[110]; // state存放每行所有无相邻1的可行状态 int dp[20][600]; // dp[i][j]:前i行中,第i行采用第j种状态的方案数 int cur[20]; // cur[i]:第i行的限制条件(二进制,1表示不可种植) inline bool ok(int x){ // 判断状态x是否无相邻1 if(x & (x << 1)) return false; return true; } void init(){ // 生成所有合法状态 top = 0; int total = 1 << N; for(int i = 0; i < total; ++i){ if(ok(i)) state[++top] = i; } } inline bool fit(int x, int k){ // 判断状态x与第k行限制是否兼容 if(x & cur[k]) return false; // x若在不可种植位为1则非法 return true; } int main(){ while(scanf("%d%d", &M, &N) != EOF){ init(); memset(dp, 0, sizeof(dp)); for(int i = 1; i <= M; ++i){ cur[i] = 0; int val; for(int j = 1; j <= N; ++j){ scanf("%d", &val); if(val == 0) // 不可种植位标记为1(逆映射) cur[i] += (1 << (N - j)); } } // 初始化第一行合法状态 for(int i = 1; i <= top; i++){ if(fit(state[i], 1)){ dp[1][i] = 1; } } // 状态转移:dp[i][k] = Σ dp[i-1][j](j为合法前序状态) for(int i = 2; i <= M; ++i){ for(int k = 1; k <= top; ++k){ if(!fit(state[k], i)) continue; for(int j = 1; j <= top; ++j){ if(!fit(state[j], i-1)) continue; if(state[k] & state[j]) continue; dp[i][k] = (dp[i][k] + dp[i-1][j]) % mod; } } } // 累加最后一行所有状态方案数 int ans = 0; for(int i = 1; i <= top; ++i){ ans = (ans + dp[M][i]) % mod; } printf("%d\n", ans); } return 0; }
四、代码核心逻辑解析
- 状态预处理:
init()
函数生成所有无相邻1的状态(如N=3时,000、001、010、100、101等),存入state
数组。
- 行限制处理:
- 输入时将不可种植的位置(0)转换为二进制1存入
cur[i]
(如输入0 1 0
对应101
,表示第1、3列不可种植)。
- 输入时将不可种植的位置(0)转换为二进制1存入
- 动态规划初始化:
- 第一行中,所有与
cur[1]
无冲突的合法状态方案数初始化为1。
- 第一行中,所有与
- 状态转移:
- 对于第i行的状态k,若其与第i行限制兼容,且与第i-1行的状态j兼容(
state[k] & state[j] == 0
),则将j的方案数累加到k上。
- 对于第i行的状态k,若其与第i行限制兼容,且与第i-1行的状态j兼容(
- 结果计算:
- 最后一行所有合法状态的方案数之和即为总方案数。
五、复杂度与优化点
- 时间复杂度:O(M×top²),其中top≤2¹²=4096(N≤12时),总复杂度约12×4096²=1.97×10⁸,可通过模运算高效计算。
- 空间复杂度:O(M×top),数组空间占用可控。
- 核心优化:通过位运算快速判断状态合法性,避免枚举所有种植组合,大幅减少计算量。
六、样例输入输出解析
输入样例:
2 3 1 1 1 0 1 0
处理过程:
- 第一行限制
cur[1]=0
(全肥沃),合法状态为000、001、010、011(非法,相邻1)、100、101、110(非法)、111(非法)→ 合法状态为000、001、010、100、101。 - 第二行限制
cur[2]=101
(第1、3列不可种植),合法状态需满足二进制位在1、3列不为1 → 合法状态为010。 - 状态转移时,第一行状态与第二行状态010兼容的有000、001、100、101,对应方案数累加:
- 000→010:合法,方案数+1;
- 001→010:合法,方案数+1;
- 100→010:合法,方案数+1;
- 101→010:合法,方案数+1;
最终总方案数为9(包含不种植的1种方案)。
- 1
信息
- ID
- 2255
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 2
- 已通过
- 1
- 上传者