题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3001
状压DP也能做,这里用的是BFS
从搜索的思路来看,这道题的难点就是状态重复访问的情况比较多,到现在我也没明白会有哪些重复访问的状态。
首先题目给出的是10个城市,每个城市都只能被访问两次,所以每一个城市可能访问的次数有0,1,2,三种,所以所有的状态数是3^10,这个数不会很大,而且起点不同,可能出现的情况也会不同,所以每一个城市都要作为起点试一次,所以总共的状态数是(3^10) * 10,这个数字显然即使全部遍历一次也不会超时,但事实上会,而且会超内存,所以在bfs的过程中一定存在状态重复走的情况,这个问题我想了很久,都觉得不太可能出现重复访问状态的情况。但事实就是如此。
既然有重复访问状态的情况,我们就只能把访问过得状态存起来,用来判断当前状态是否走过,以此避免重复放入队列,造成超时和内存超限。
这里的状态主要有三部分组成,访问城市的状态,当前的起点,当前的总消费。为什么是这三个?访问城市的状态相同,起点不同,结果也会大不相同,如果起点也相同,消费不同,情况也会不同。
所以判重的条件应该是:如果访问过 访问城市状态和起点都相同的状态,并且总消费比现在的要小或者等于,就跳过。否则就把这个状态放到队列里面,并且更新状态的总消费。
一开始想图个方便用 map<string, pair<int, int> > 来存三个部分的数据,但是不管怎么优化其他的部分,就是会超时,所以用数组存相应的数据,要用时随取随用,这才AC了。
#include <iostream>
#include <queue>
#include <cstring>
#include <string>
#include <map>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
const int INF = 0x3f3f3f3f;
const int Maxn = 4e5+10;
struct Node {
int cur, cost, stat;
};
int cost[15][15], N, M, ans, sum[60000][12], u0[12];
int state[60000][12];
queue<Node> qu;
void bfs(int v) {
while(!qu.empty()) qu.pop();
Node tmp;
tmp.cost = 0; tmp.cur = v;
tmp.stat = u0[v];
qu.push(tmp);
bool ok;
while (!qu.empty()) {
tmp = qu.front(); qu.pop();
ok = true;
for(int i = 1; i <= N; ++i) {
if(state[tmp.stat][i] == 0) {
ok = false; break;
}
}
if(ok) {
ans = min(ans, tmp.cost); continue;
}
for(int i = 1; i <= N; ++i) {
if(i == tmp.cur || state[tmp.stat][i] > 1 || cost[tmp.cur][i] == -1) continue;
Node now = tmp;
now.stat += u0[i];
now.cur = i; now.cost += cost[tmp.cur][i];
if(now.cost >= ans) continue;
if(sum[now.stat][now.cur] != 0 && sum[now.stat][now.cur] <= now.cost) continue;
sum[now.stat][now.cur] = now.cost;
qu.push(now);
}
}
}
void init() {
for(int i = 1; i <= 11; ++i) u0[i] = pow(3, i-1);
memset(state, 0, sizeof(state));
for(int i = 1; i < u0[11]; ++i) {
int tmp = i, p = 1;
while (tmp) {
state[i][p++] = tmp%3;
tmp /= 3;
}
}
}
int main(void)
{
init();
while (scanf("%d%d", &N, &M) != EOF) {
memset(cost, -1, sizeof(cost));
memset(sum, 0, sizeof(sum));
int u, v, c;
for(int i = 0; i < M; ++i) {
scanf("%d%d%d", &u, &v, &c);
if(cost[u][v] == -1) cost[u][v] = cost[v][u] = c;
else cost[u][v] = cost[v][u] = min(cost[u][v], c);
}
ans = INF;
for(int i = 1; i <= N; ++i) bfs(i);
if(ans == INF) printf("-1\n");
else printf("%d\n", ans);
}
return 0;
}