C++动态规划:三维网格图着色问题的高效解法
·
三维网格图着色问题的高效动态规划解法
问题分析
给定一个 $X \times Y \times Z$ 的三维网格,每个网格点需着色,满足:
- 层内约束:同一层中相邻点(上下左右)颜色不同
- 层间约束:相邻层相同位置的点颜色不同 使用 $k$ 种颜色,求合法着色方案总数。动态规划的核心是高效处理状态空间。
状态设计
- 状态压缩:将每层 $X \times Y$ 网格展开为长度为 $X \times Y$ 的一维数组,用整数 $mask$ 表示着色状态($k$ 进制数)。
- 合法状态:$valid[mask]$ 标记满足层内约束的状态。
- 状态转移:$dp[z][i]$ 表示第 $z$ 层使用第 $i$ 个合法状态的方案数。
关键优化
- 预处理合法状态:提前计算所有满足层内约束的 $mask$,减少无效枚举。
- 兼容性矩阵:预计算层间状态转移关系 $compatible[i][j]$,避免重复检查。
- 状态空间压缩:仅存储合法状态,降低空间和时间复杂度。
算法步骤
- 输入参数:网格尺寸 $X, Y, Z$ 和颜色数 $k$。
- 预计算:
- $base$ 数组:存储 $k^i$ 用于快速访问颜色。
- $valid$ 数组:标记满足层内约束的状态。
- $valid_masks$:收集所有合法状态。
- 兼容性矩阵:对每对合法状态检查层间约束。
- 动态规划:
- 初始化:$dp[0][i] = 1$(第一层合法状态)。
- 转移:$dp[z][j] = \sum_{compatible[i][j]} dp[z-1][i]$。
- 输出结果:$\sum dp[Z-1][i]$。
复杂度分析
- 时间:$O(Z \cdot n_{\text{valid}}^2)$,其中 $n_{\text{valid}}$ 是每层合法状态数。
- 空间:$O(Z \cdot n_{\text{valid}} + n_{\text{valid}}^2)$。
C++ 代码实现
#include <iostream>
#include <vector>
using namespace std;
int main() {
int X, Y, Z, k;
cin >> X >> Y >> Z >> k;
int total = X * Y;
long long total_states = 1;
for (int i = 0; i < total; i++) {
total_states *= k;
}
vector<long long> base(total + 1, 1);
for (int i = 1; i <= total; i++) {
base[i] = base[i - 1] * k;
}
vector<bool> valid(total_states, false);
vector<vector<int>> colorAll(total_states, vector<int>(total));
vector<long long> valid_masks;
for (long long mask = 0; mask < total_states; mask++) {
vector<int> colors(total);
long long m = mask;
for (int pos = 0; pos < total; pos++) {
colors[pos] = m % k;
m /= k;
}
colorAll[mask] = colors;
bool flag = true;
for (int pos = 0; pos < total; pos++) {
int row = pos / Y;
int col = pos % Y;
if (col + 1 < Y && colors[pos] == colors[pos + 1]) {
flag = false;
break;
}
if (row + 1 < X && colors[pos] == colors[pos + Y]) {
flag = false;
break;
}
}
if (flag) {
valid[mask] = true;
valid_masks.push_back(mask);
}
}
int n_valid = valid_masks.size();
vector<vector<int>> valid_color(n_valid, vector<int>(total));
for (int i = 0; i < n_valid; i++) {
valid_color[i] = colorAll[valid_masks[i]];
}
vector<vector<bool>> compatible(n_valid, vector<bool>(n_valid, false));
for (int i = 0; i < n_valid; i++) {
for (int j = 0; j < n_valid; j++) {
bool flag = true;
for (int pos = 0; pos < total; pos++) {
if (valid_color[i][pos] == valid_color[j][pos]) {
flag = false;
break;
}
}
compatible[i][j] = flag;
}
}
vector<vector<long long>> dp(Z, vector<long long>(n_valid, 0));
for (int i = 0; i < n_valid; i++) {
dp[0][i] = 1;
}
for (int z = 1; z < Z; z++) {
for (int j = 0; j < n_valid; j++) {
long long sum = 0;
for (int i = 0; i < n_valid; i++) {
if (compatible[i][j]) {
sum += dp[z - 1][i];
}
}
dp[z][j] = sum;
}
}
long long ans = 0;
for (int i = 0; i < n_valid; i++) {
ans += dp[Z - 1][i];
}
cout << ans << endl;
return 0;
}
使用说明
- 输入格式:依次输入 $X$, $Y$, $Z$, $k$(空格分隔)。
- 输出格式:输出合法着色方案总数。
- 适用场景:$X \times Y$ 较小($\leq 10$),$Z$ 中等($\leq 100$),$k$ 适中($\leq 5$)。
- 优化效果:通过状态压缩和预计算,将指数级问题转化为多项式级。
示例输入:
2 2 2 2
示例输出:2(对应棋盘染色方案)
更多推荐


所有评论(0)