【题意】
给定一个n个顶点组成的带权的有向图的距离矩阵d[i,j],要求从0开始结果所有点一次回到0,问所经过边的总权重的最小值为多少
【思路】
旅行商问题TSP,状态压缩dp,记忆化dp
【分析】
假设当前已经访问过的顶点集合为S,(起点0当作还未访问过的点),当前所在的顶点为v,用dp[S][v]表示从v出发访问剩余所有的点最终回到0的最小权重和。
dp[V][0]=0;
dp[S][u]=min(dp[S|{v}[v]]+a[u][v]) ( v不属于S )
【代码】
#include<bits/stdc++.h>
using namespace std;
const int maxn = 11;
const int inf = 0x3f3f3f3f;
int a[maxn][maxn];
int dp[1 << maxn][maxn];//dp[i][j]表示已经经过的节点集合为i,当前位于j,从j出发访问剩余节点回到0的最小距离
int n;
int dfs(int S, int u) {
if (dp[S][u] != -1)return dp[S][u];
if (S == (1 << (n+1)) - 1 && u == 0) return dp[S][u] = 0;
int res = inf;
for (int v = 0; v <= n; v++) {
if (!(S >>v & 1)) {
res = min(res, dfs(S | (1 << v), v)+a[u][v]);
}
}
return dp[S][u] = res;
}
int main() {
while (~scanf("%d", &n),n) {
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= n; j++) {
scanf("%d", &a[i][j]);
}
}
for (int k = 0; k <= n; k++) {
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= n; j++) {
a[i][j] = min(a[i][j], a[i][k] + a[k][j]);
}
}
}
memset(dp, -1, sizeof(dp));
printf("%d\n", dfs(0, 0));
}
}
循环代码
int dp[1 << maxn][maxn];//dp[i][j]表示已经经过的节点集合为i,当前位于j,从j出发访问剩余节点回到0的最小距离
int n;
//转移:dp[S][v]=min(dp[S][v],dp[S|1<<u][u]+d[v][u]);
void solve() {
memset(dp, inf, sizeof(dp));
dp[(1 << n ) - 1][0] = 0;
for (int S = (1 << n) - 2; S >= 0; S--) {
for (int v = 0; v <= n; v++) {
for (int u = 0; u <= n; u++) {
if(!(S>>u&1))
dp[S][v] = min(dp[S][v], dp[S | (1 << u)][u] + a[v][u]);
}
}
}
printf("%d\n", dp[0][0]);
}