Description
给你一张n个点,m条边的图,有K个必经点,分别是:2,3,…,K+1,经过一个点,可以选择停留,也可以不停留,这里的毕竟是指停留,给出q个限制条件,要求满足在某个点停留之前,某个点已经停留过。
Sample Input
8 15 4
1 2 3
1 3 4
1 4 4
1 6 2
1 7 3
2 3 6
2 4 2
2 5 2
3 4 3
3 6 3
3 8 6
4 5 2
4 8 6
5 7 4
5 8 6
3
2 3
3 4
3 5
Sample Output
19
首先你可以先预处理出K个点之间互相的最短路关系。
然后考虑状压,然后就是一个比较水的状压题了。。。
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
int _min(int x, int y) {return x < y ? x : y;}
int _max(int x, int y) {return x > y ? x : y;}
int read() {
int s = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
return s * f;
}
struct node {
int x, d;
friend bool operator < (node a, node b) {return a.d > b.d;}
}; priority_queue<node> q;
struct edge {
int x, y, c, next;
} e[410000]; int len, last[21000];
int K, s[22], id[22], D[22][22], d[21000];
int f[21][1048576];
bool v[21000];
void ins(int x, int y, int c) {
e[++len].x = x, e[len].y = y; e[len].c = c;
e[len].next = last[x], last[x] = len;
}
void dij(int st, int hh) {
memset(v, 0, sizeof(v));
memset(d, 63, sizeof(d)); d[st] = 0;
node tmp; tmp.x = st, tmp.d = 0;
q.push(tmp);
while(!q.empty()) {
node now = q.top(); q.pop();
int x = now.x;
if(v[x]) continue; v[x] = 1;
for(int k = last[x]; k; k = e[k].next) {
int y = e[k].y;
if(d[y] > d[x] + e[k].c) {
d[y] = d[x] + e[k].c;
if(!v[y]) {tmp.x = y, tmp.d = d[y]; q.push(tmp);}
}
}
} for(int i = 1; i <= K + 1; i++) if(id[i] != st) D[hh][i] = d[id[i]];
}
int main() {
int n = read(), m = read(); K = read();
for(int i = 1; i <= m; i++) {
int x = read(), y = read(), c = read();
ins(x, y, c), ins(y, x, c);
} id[0] = 1;
for(int i = 1; i <= K; i++) id[i] = i + 1;
id[K + 1] = n; for(int i = 0; i <= K; i++) dij(id[i], i);
if(K == 0) {printf("%d\n", D[0][1]); return 0;}
int q = read();
for(int i = 1; i <= q; i++) {
int x = read() - 1, y = read() - 1;
if(x > K || y > K) continue;
s[y] |= 1 << x - 1;
} memset(f, -1, sizeof(f));
f[0][0] = 0;
for(int i = 0; i < (1 << K); i++) {
for(int j = 0; j <= K; j++) if(f[j][i] != -1){
for(int k = 1; k <= K; k++) if((i & s[k]) == s[k] && !(i << k - 1 & 1)){
if(f[k][i | (1 << k - 1)] == -1) f[k][i | (1 << k - 1)] = f[j][i] + D[j][k];
else f[k][i | (1 << k - 1)] = _min(f[k][i | (1 << k - 1)], f[j][i] + D[j][k]);
}
}
} int ans = 999999999;
for(int i = 1; i <= K; i++) if(f[i][(1 << K) - 1] != -1){
ans = _min(ans, f[i][(1 << K) - 1] + D[i][K + 1]);
} printf("%d\n", ans);
return 0;
}