题目链接 : 点击打开链接
题意 : 0的位置是商店,1,2,3……表示城市,你需要经过所有的城市然后再回到商店,求出一个时间花费的最小值。
思路:刚开始的我只能想到一个暴力搜索的思路,从0开始dfs,然后当找到的点等于城市的个数的时候,在判断是否可以到0,然后结束。不过我没有去实现(才不是懒,是觉得可能超时QAQ)。然后去网上翻了题解,发现是一道状压DP的题。然后看来好长时间,才看明白。
其实是从暴力搜索的基础考虑的,如我们找 0 -> 1 ->2 之后会重复找很多个没找到的城市。很多都是没用的。如果我们可以把到每一个城市的最优解记录下路,然后再去找下一个城市,这样会避免找到很多多余的。
所以DP【i】【j】 表示当状态为i时,到达j城市的最小时间花费。
转移方程: DP【i】【j】 = min(DP【上一个可以到达i的状态】【j】 + dis【j】【i】,DP【i】【j】) ;
当然了,首先用最短路的算法求出一个点之间的最短路
#include<iostream>
#include<algorithm>
using namespace std;
#define INF 100000000
int dis[12][12]; // 记录点之间的最短路
int dp[1 << 11][12]; // i状态下,到达j点的最短路。 到
int n,ans;
int main(){
while(cin >> n && n){
for(int i = 0 ; i <= n ; i++)
for(int j = 0 ; j <= n ; j ++)
cin >> dis[i][j];
for(int k = 0 ; k <= n ;k ++ ) // folyd求最短路
for(int i = 0 ; i <= n; i++)
for(int j = 0 ; j <= n ; j++)
dis[i][j] = min (dis[i][j],dis[i][k]+dis[k][j]);
for(int S = 0 ; S <= (1<<n)-1 ; S ++) // 第一层for循环是找符合条件的状态。 解释一下为什么是0 到 (1 <<n )-1,因为i位置的点表示的是i+1城市的状态。所以-1
for(int i = 1 ; i <= n ; i++){ // 第二次for循环是到达,i城市。
if(S & (1 << (i-1))){ //看状态S是否有没有经过i城市, 若没有,肯定没有讨论的必要
if(S == (1 << (i-1))) // 这个是判断是否 状态为只经过i这个城市,若经过的话,很明显这种状态的最小值就是从0点直接到i的最小值
dp[S][i] = dis[0][i];
else { // 有多个城市的状态
dp[S][i] = INF ; // 初始化为INF
for(int j = 1; j <= n ; j ++){ // 类似floyd的想法,看是否能通过一个j来使这个到达i城市的时间最小。
if(S & (1 << (j-1)) && j != i) // 判断 S状态下中 j 城市有没有经过,若没有经过的话不在讨论范围,因为这两者的状态没有练习。
dp[S][i] = min (dp[S^(1 << (i-1))][j] + dis[j][i],dp[S][i]); // 解释一下 ^运算,1 ^1 = 0,所以根据这个性质来取消对i城市的标记。
}
}
}
}
ans = dp[(1 << n) - 1][1] + dis[1][0];
for(int i = 2 ; i <= n ; i++) // 从最后的结果中找出一个最小值
if(dp[(1 << n) - 1][i] + dis[i][0] < ans)
ans = dp[(1 << n) - 1][i] + dis[i][0];
cout << ans << endl;
}
return 0;
}