P1294 高手去散步 - 状态压缩动态规划解法
🌟 P1294 高手去散步 - 状态压缩动态规划解法
解题思路
在无向图中寻找最长不重复路径,要求每个节点仅访问一次。数据规模较小(n ≤ 20),采用状态压缩动态规划(DP)高效解决,通过二进制状态表示和位运算优化计算效率。
核心算法思想
状态表示
- 二进制状态:用整数
state的二进制位表示已访问节点集合(如1010表示访问了第1、3号节点) - DP定义:
dp[state][i]表示在状态state下,当前位于节点i时的最大路径长度
状态转移
- 枚举当前状态:遍历所有可能的二进制状态
- 邻接扩展:对每个节点
i,尝试访问其所有未访问的邻接节点j - 更新状态:
dp[新状态][j] = max(dp[新状态][j], dp[当前状态][i] + 边权)
代码实现
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
// 邻接表存储图
vector<vector<pair<int, int>>> graph(n);
for (int i = 0; i < m; i++) {
int u, v, w;
cin >> u >> v >> w;
u--; v--; // 转换为0-indexed
graph[u].push_back({v, w});
graph[v].push_back({u, w});
}
// 状态总数:2^n
int total_states = 1 << n;
// DP数组初始化(-1表示不可达)
vector<vector<int>> dp(total_states, vector<int>(n, -1));
int ans = 0;
// 初始化:每个节点作为起点
for (int i = 0; i < n; i++) {
dp[1 << i][i] = 0;
}
// 状态转移
for (int state = 0; state < total_states; state++) {
for (int i = 0; i < n; i++) {
if (dp[state][i] == -1) continue; // 跳过无效状态
// 遍历邻接节点
for (auto [j, w] : graph[i]) {
if (state & (1 << j)) continue; // 已访问则跳过
int new_state = state | (1 << j);
int new_val = dp[state][i] + w;
if (new_val > dp[new_state][j]) {
dp[new_state][j] = new_val;
ans = max(ans, new_val); // 实时更新答案
}
}
}
}
cout << ans << endl;
return 0;
}
算法优化点
1. 状态压缩技术
- 位运算高效处理:集合操作转化为位运算(如
state | (1 << j)表示添加节点j) - 空间优化:用整数代替集合,减少存储开销
2. 邻接表优化
- 精准遍历:仅处理实际存在的边,避免无效循环
- 时间复杂度优化:从O(n²·2ⁿ)降至O(m·2ⁿ)
3. 动态规划剪枝
- 跳过无效状态:
dp[state][i] == -1时直接跳过 - 实时更新答案:在状态转移过程中维护全局最大值
复杂度分析
| 维度 | 分析说明 |
|---|---|
| 时间复杂度 | O(m·2ⁿ) ,n=20时约5×10⁷次操作 |
| 空间复杂度 | O(n·2ⁿ) ,n=20时约80MB内存占用 |
算法演示
输入样例
4 6
1 2 10
2 3 20
3 4 30
4 1 40
1 3 50
2 4 60
关键状态转移
- 初始状态:
0010(节点2),路径长度0 - 访问节点4:状态→
1010,长度+60 → 60 - 访问节点1:状态→
1110,长度+40 → 100 - 访问节点3:状态→
1111,长度+50 → 150
拓展应用
适用场景
| 问题类型 | 解法调整要点 |
|---|---|
| 哈密顿路径问题 | 强制状态终态为全1 |
| 最短路径问题 | 将max改为min并初始化INF |
| 带权重约束路径 | 增加状态维度记录当前权重 |
| 网络拓扑规划 | 结合业务约束设计状态表示 |
大规模问题优化
- 滚动数组:压缩空间至O(n·2ⁿ⁻¹)
- 分治策略:将图分解为子图分别处理
- 剪枝优化:提前终止不可能达到最优解的状态
算法心得
状态压缩DP通过二进制魔法将复杂的集合操作转化为高效的位运算,在解决中小规模组合优化问题时展现出惊人威力。其核心要义在于:
- 状态设计:找到问题本质的数学表达
- 转移方程:定义清晰的状态演化规则
- 实现优化:利用位运算提升计算效率
学习建议:从n=4的案例手动模拟状态转移表,深入理解二进制状态与实际问题之间的映射关系。🧠⚡️
Sanhai AI