售货员的难题
Description
某乡有N个村庄(1<N<40),有一个售货员,他要到各个村庄去售货,各个村庄之间的路程S(0<S<10000)是已知的,且A村到B村与B村到A村的路大多不同.为了提高效率,他从商店出发到每个村庄一次,然后返回商店所在的村,假设商店所在的村庄为1,他不知道选择什么样的路线才能使所走过的路程最短.请你帮他选择一条最短的路.
Input
第一行为一个整数n(n<=40),表示城市的总个数。
接下来是一个n*n的矩阵,用来表示城市间的连通情况以及花费,例如path[i][j]=len,len=0表示从城市i到城市j没有通路,len>0表示从i到j的路程长度为len。
Output
输出一行,对于给定的问题,如果找到了最小路程(花费),输出该最小花费,如果没有通路可以到达每个城市,则输出-1。
Sample Input
3
0 2 1
1 0 2
2 1 0
Sample Output
3
Hint
样例提示:
3 {村庄数}
0 2 1 {村庄1到各村的路程}
1 0 2 {村庄2到各村的路程}
2 1 0 {村庄3到各村的路程}
Source
搜索, Np问题, 状压DP, 模拟退火
Solution
用一个二进制数表示当前状态下访问每一个村庄的状态(不需考虑访问村庄的顺序,只用考虑状态即可),用0表示没有访问,1表示已经访问过
设置一个二维数组dp[i][j],其中i为二进制表示的村庄访问状态的十进制形式,j表示当前所在的村庄,即下一次访问需要从第j个村庄开始
对于每一种状态,存在转移方程dp[i + 1 << j][j] = min(dp[i + 1 << j][j], dp[i][j] + d[j][v]),当然前提是i状态下的j访问过,v没有访问过
Code
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <stack>
#include <map>
#include <vector>
#include <queue>
#define L 50
#define INF 1000000009
#define get(i, j) (i / len[j - 1]) % 2
using namespace std;
inline int gi() {
char cj = getchar();
int ans = 0, f = 1;
while (cj < '0' || cj > '9') {
if (cj == '-') f = -1;cj = getchar();
}
while (cj >= '0' && cj <= '9') ans = ans * 10 + cj - '0', cj = getchar();
return f * ans;
}
int n, len[L], d[L][L], dp[(1 << 16)][L], ans = INF;
int main() {
len[0] = 1;
for (int i = 1; i <= 20; ++i) len[i] = len[i - 1] * 2;
memset(dp, INF, sizeof(dp));
dp[1][1] = 0;
n = gi();
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
d[i][j] = gi();
for (int i = 0; i <= len[n]; ++i) {
for (int j = 1; j <= n; ++j) {
if (get(i, j) == 0) continue;
for (int v = 1; v <= n; ++v) {
if (get(i, v)) continue;
dp[i + len[v - 1]][v] = min(dp[i + len[v - 1]][v], dp[i][j] + d[j][v]);
}
}
}
for (int j = 2; j <= n; ++j) ans = min(ans, dp[len[n] - 1][j] + d[j][1]);
if (ans >= INF) ans = -1;
printf("%d\n", ans);
return 0;
}
Summary
第一次提交的时候被数据范围骗了,空间开爆了
第二次提交的时候发现初始化除了问题:不用for (int i = 1; i <= 20; ++i) dp[len[i - 1]][i] = 0,更新dp数组,因为这句话的意思是考虑每一个村庄作为初始村庄的最小费用,而此题要求从1号村庄开始
另外不要忘记了最后还要回到1号村庄