三维网格图着色问题的高效动态规划解法

问题分析

给定一个 $X \times Y \times Z$ 的三维网格,每个网格点需着色,满足:

  1. 层内约束:同一层中相邻点(上下左右)颜色不同
  2. 层间约束:相邻层相同位置的点颜色不同 使用 $k$ 种颜色,求合法着色方案总数。动态规划的核心是高效处理状态空间。
状态设计
  • 状态压缩:将每层 $X \times Y$ 网格展开为长度为 $X \times Y$ 的一维数组,用整数 $mask$ 表示着色状态($k$ 进制数)。
  • 合法状态:$valid[mask]$ 标记满足层内约束的状态。
  • 状态转移:$dp[z][i]$ 表示第 $z$ 层使用第 $i$ 个合法状态的方案数。
关键优化
  1. 预处理合法状态:提前计算所有满足层内约束的 $mask$,减少无效枚举。
  2. 兼容性矩阵:预计算层间状态转移关系 $compatible[i][j]$,避免重复检查。
  3. 状态空间压缩:仅存储合法状态,降低空间和时间复杂度。
算法步骤
  1. 输入参数:网格尺寸 $X, Y, Z$ 和颜色数 $k$。
  2. 预计算
    • $base$ 数组:存储 $k^i$ 用于快速访问颜色。
    • $valid$ 数组:标记满足层内约束的状态。
    • $valid_masks$:收集所有合法状态。
  3. 兼容性矩阵:对每对合法状态检查层间约束。
  4. 动态规划
    • 初始化:$dp[0][i] = 1$(第一层合法状态)。
    • 转移:$dp[z][j] = \sum_{compatible[i][j]} dp[z-1][i]$。
  5. 输出结果:$\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;
}

使用说明
  1. 输入格式:依次输入 $X$, $Y$, $Z$, $k$(空格分隔)。
  2. 输出格式:输出合法着色方案总数。
  3. 适用场景:$X \times Y$ 较小($\leq 10$),$Z$ 中等($\leq 100$),$k$ 适中($\leq 5$)。
  4. 优化效果:通过状态压缩和预计算,将指数级问题转化为多项式级。

示例输入2 2 2 2
示例输出2(对应棋盘染色方案)

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐