以下是一个使用动态规划解决大规模网格涂色问题的C++实现。问题描述:给定一个 $m \times n$ 的网格,每个格子可涂 $k$ 种颜色,要求相邻格子(上下左右)颜色不同。使用状态压缩和矩阵快速幂优化,时间复杂度为 $O(s^3 \log m)$,其中 $s$ 为单行合法状态数,适用于列数 $n \leq 3$ 的场景(若 $n > 3$ 需调整参数)。

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

const int MOD = 1000000007;

// 将整数状态转换为颜色数组
vector<int> toColorArray(int state, int n, int k) {
    vector<int> colors(n);
    for (int i = 0; i < n; ++i) {
        colors[i] = state % k;
        state /= k;
    }
    return colors;
}

// 检查行内相邻颜色是否不同
bool isValidRow(vector<int>& colors) {
    for (int i = 0; i < colors.size() - 1; ++i) {
        if (colors[i] == colors[i + 1]) 
            return false;
    }
    return true;
}

// 检查两行在每列上颜色是否不同
bool isValidAdjacent(vector<int>& colors1, vector<int>& colors2) {
    for (int i = 0; i < colors1.size(); ++i) {
        if (colors1[i] == colors2[i]) 
            return false;
    }
    return true;
}

// 矩阵乘法
vector<vector<long long>> multiplyMatrix(vector<vector<long long>>& A, vector<vector<long long>>& B) {
    int n = A.size(), p = A[0].size(), m = B[0].size();
    vector<vector<long long>> C(n, vector<long long>(m, 0));
    for (int i = 0; i < n; ++i) {
        for (int k = 0; k < p; ++k) {
            if (A[i][k]) {
                for (int j = 0; j < m; ++j) {
                    C[i][j] = (C[i][j] + A[i][k] * B[k][j]) % MOD;
                }
            }
        }
    }
    return C;
}

// 矩阵快速幂
vector<vector<long long>> matrixPower(vector<vector<long long>> base, int power) {
    int n = base.size();
    vector<vector<long long>> res(n, vector<long long>(n, 0));
    for (int i = 0; i < n; ++i) res[i][i] = 1;
    while (power) {
        if (power & 1) res = multiplyMatrix(res, base);
        base = multiplyMatrix(base, base);
        power >>= 1;
    }
    return res;
}

// 矩阵乘向量
vector<long long> multiplyMatrixVector(vector<vector<long long>>& mat, vector<long long>& vec) {
    int n = mat.size(), m = vec.size();
    vector<long long> res(n, 0);
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < m; ++j) {
            res[i] = (res[i] + mat[i][j] * vec[j]) % MOD;
        }
    }
    return res;
}

int main() {
    int m, n, k;
    cin >> m >> n >> k;  // 输入行数、列数、颜色数

    // 计算总状态数并生成合法状态
    int totalStates = 1;
    for (int i = 0; i < n; ++i) totalStates *= k;
    vector<int> validStates;
    for (int state = 0; state < totalStates; ++state) {
        vector<int> colors = toColorArray(state, n, k);
        if (isValidRow(colors)) {
            validStates.push_back(state);
        }
    }
    int s = validStates.size();

    // 处理单行情况
    if (m == 1) {
        cout << s % MOD << endl;
        return 0;
    }

    // 构建转移矩阵T
    vector<vector<long long>> T(s, vector<long long>(s, 0));
    for (int i = 0; i < s; ++i) {
        vector<int> colors_i = toColorArray(validStates[i], n, k);
        for (int j = 0; j < s; ++j) {
            vector<int> colors_j = toColorArray(validStates[j], n, k);
            if (isValidAdjacent(colors_i, colors_j)) {
                T[j][i] = 1;  // 状态i -> 状态j的转移
            }
        }
    }

    // 初始化向量(第一行方案数)
    vector<long long> v(s, 1);

    // 计算T^(m-1)并应用
    vector<vector<long long>> T_exp = matrixPower(T, m - 1);
    vector<long long> resultVec = multiplyMatrixVector(T_exp, v);

    // 求和输出
    long long ans = 0;
    for (long long num : resultVec) {
        ans = (ans + num) % MOD;
    }
    cout << ans << endl;

    return 0;
}

算法说明

  1. 问题分析:网格涂色需满足相邻格子颜色不同,使用状态压缩表示单行颜色组合。
  2. 状态定义
    • 单行状态用整数表示($k$ 进制编码)。
    • $dp[i][j]$ 表示第 $i$ 行为状态 $j$ 时的方案数。
  3. 动态规划
    • 预处理:生成所有行内合法的状态(相邻列颜色不同)。
    • 转移矩阵:构建矩阵 $T$,其中 $T_{ji} = 1$ 当且仅当状态 $i$(上一行)与状态 $j$(下一行)满足列间颜色不同。
  4. 矩阵快速幂
    • 将递推关系转化为矩阵幂运算:$dp_{m} = T^{m-1} \cdot dp_1$。
    • 时间复杂度从 $O(m \cdot s^2)$ 优化至 $O(s^3 \log m)$。
  5. 结果计算:对最终状态向量求和得到总方案数。

使用示例

输入:

2 2 3  // 2行2列,3种颜色

输出:

54     // 总方案数

复杂度

  • 空间复杂度:$O(s^2)$,存储转移矩阵。
  • 时间复杂度:$O(s^3 \log m)$,其中 $s = k \cdot (k-1)^{n-1}$ 为单行合法状态数。

注意:当 $n > 3$ 时,$s$ 可能过大,需优化矩阵乘法或使用其他算法。

Logo

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

更多推荐