状压dp
拿一道比较经典的例题来作为引入
旅行商问题(TSP)
给定 \(n\) 个城市和每对城市之间的距离,求解访问每一座城市一次并回到起始城市的最短回路。$ n \leq 18$
首先最暴力的解法就是去进行一个 \(O(n!)\) 的 \(dfs\),显然是无法通过此题的。这里利用状态压缩的想法, \(dp_{i,j}\) 代表访问的城市的状态为 \(i\) 并且当前在 \(j\) 座城市的最短路,那么答案显然是 \(dp_{2^n-1,1}\),转移的时候一定是可以从已访问城市数量少的状态转移到已访问城市数量多的状态。这样我们就获得了一个 \(O(n^2 2^n)\) 的做法。
AC code:
#include <bits/stdc++.h>
using namespace std;
template<typename T> bool setmin(T &a, T b) { return (a > b ? a = b : false); }
int main() {
ios_base::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
int n, m; cin >> n >> m;
for (int i = 0; i < m; i++) {
int u, v, w; cin >> u >> v >> w;
d[u][v] = d[v][u] = w;
}
vector<vector<ll>> dp(1 << n, vector<ll>(n, 1e18));
dp[1][1] = 0;
for (int mask = 0; mask < 1 << n; mask++) {
for (int i = 0; i < n; i++) {
if (mask >> i & 1) {
for (int j = 0; j < n; j++) {
if (!(mask >> j & 1))
setmin(dp[mask | (1 << j)][j], dp[mask][i] + d[i][j]);
}
}
}
}
ll ans = 1e18;
for (int i = 0; i < r; i++) {
setmin(ans, dp[(1 << r) - 1][i] + d[i][1]);
}
cout << ans << '\n';
return 0;
}
总结一下,一般的状压dp的题目数据限制比较明显,对于数据量比较小的一维我们可以通过对其进行状压来进行暴力破解,同时此类题目需要掌握利用滚动数组来优化空间复杂度的技巧,特别是在轮廓线dp中。
problem set: