这是状压DP中常见的一类问题(TSP问题)(废话)
题目大意:
给你一个n+1个点的有向完全图,现在要你从0号节点出发走过1-n号点至少一次,再返回0号点的最少时间,1<=n<=10
题意分析:
由于题目给出的是有向完全图并且以邻接矩阵的形式给出,所以我们先floyd预处理出最短的路径
设状态dp [ state ] [ i ]表示从0出发,走完state中的所有的点,最后停在i点的最短距离
枚举 i 点之前的一个停留点 j ,得到如下的状态转移方程:
dp [ state ] [ i ] = min { dp [ state' ] [ j ] + dis [ i ] [ j ] }
其中 state' 是 state 去掉 i 点的集合 , state必然包含 i 和 j
我们尝试证明上述方程的正确性:
每次从子集中转移,那么每次转移时,每个子集都有答案,就是正确的
边界:dp [ { i } ] [ i ] = dis [ 0 ] [ i ]
最后输出最后的状态到0点的最小代价即可
/* A - Hie with the Pie */
/* TSP问题,状压DP,状态表示去过的城市 */
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
int n;
int dis[15][15];
/* 第一维表示状态,第二维表示要到的城市 */
int dp[1 << 12][15];
int main()
{
while (~scanf("%d", &n) && n)
{
std::memset(dis, 0, sizeof(dis));
std::memset(dp, -1, sizeof(dp));
for (int i = 0; i <= n; i++)
for (int j = 0; j <= n; j++)
scanf("%d", &dis[i][j]);
for (int k = 0; k <= n; k++) /* Floyd跑最短路,预处理 */
for (int i = 0; i <= n; i++)
for (int j = 0; j <= n; j++)
dis[i][j] = std::min(dis[i][j], dis[i][k] + dis[k][j]);
dp[1][0] = 0; /* 边界 */
for (int i = 1; i < (1 << (n + 1)); i++) /* 状态枚举 */
{
i |= 1;
for (int j = 0; j <= n; j++)
if (dp[i][j] != -1)
for (int k = 0; k <= n; k++)
if (j != k && (dp[i | (1 << k)][k] == -1 || (dp[i | (1 << k)][k] > dp[i][j] + dis[j][k])))
dp[i | (1 << k)][k] = dp[i][j] + dis[j][k];
}
std::cout << dp[(1 << (n + 1)) - 1][0] << '\n';
}
}