C++实现:动态规划解决大规模网格涂色问题
·
以下是一个使用动态规划解决大规模网格涂色问题的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;
}
算法说明
- 问题分析:网格涂色需满足相邻格子颜色不同,使用状态压缩表示单行颜色组合。
- 状态定义:
- 单行状态用整数表示($k$ 进制编码)。
- $dp[i][j]$ 表示第 $i$ 行为状态 $j$ 时的方案数。
- 动态规划:
- 预处理:生成所有行内合法的状态(相邻列颜色不同)。
- 转移矩阵:构建矩阵 $T$,其中 $T_{ji} = 1$ 当且仅当状态 $i$(上一行)与状态 $j$(下一行)满足列间颜色不同。
- 矩阵快速幂:
- 将递推关系转化为矩阵幂运算:$dp_{m} = T^{m-1} \cdot dp_1$。
- 时间复杂度从 $O(m \cdot s^2)$ 优化至 $O(s^3 \log m)$。
- 结果计算:对最终状态向量求和得到总方案数。
使用示例
输入:
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$ 可能过大,需优化矩阵乘法或使用其他算法。
更多推荐


所有评论(0)