最小费用流。
题目实际求两天1到n个路径,且每个暗哨只经过一次,求最少的时间。
不考虑时间的问题,则求的是是否存在两条没有重复暗哨点的路径,用网络流来解,要求最少的时间,则用最小费用流解法,拆点,保证最大流为2,在此基础上,保证费用(时间)最小即可。
题目求的是1到n和n到1这一条路线,并且除1和n以外,中间不能经过重复的点。我们可以在u和v之间连接两条有向边,<u,v>和<v,u>流量是1,费用是u v 之间的时间。因为不能经过重复的点,这样的话需要拆点,将u拆成u1和u2,u1为输入点,u2为输出点,形成一条有向边<u1,u2>,容量为1,费用为0.
对于u和v之间的边,他们的时间为w,连接有向边<u2,v1>, <v2,u1>,容量为都为1,费用都为w。
然后求最大流即可。
注意:如果1到n有直接路径,最优解可能是不经过暗哨,所以1和n拆点后连接的是流量无穷的边(大于2即可),费用为1到n的时间。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define INF 0x1f1f1f
#define clr(x) memset(x,0,sizeof(x))
const int maxn = 550;
const int maxm = maxn*(maxn+6);
struct node{
int next, from, to, cost, flow;
}e[maxm];
int tot;
int head[maxn];
void add(int s, int t, int wi, int fl){
e[tot].next = head[s];
e[tot].from = s;
e[tot].to = t;
e[tot].cost = wi;
e[tot].flow = fl;
head[s] = tot++;
e[tot].next = head[t];
e[tot].from = t;
e[tot].to = s;
e[tot].cost = -wi;
e[tot].flow = 0;
head[t] = tot++;
}
int q[maxm];
int pre[maxn];
int dis[maxn];
int v[maxn];
bool spfa(int src, int sink, int n){
int i, x, front, rear, k;
front = rear = 0;
for (i=0; i<=n; i++){
dis[i] = INF;
}
clr(v);
pre[src] = -1;
q[rear++] = src;
dis[src] = 0;
v[src] = 1;
while (front < rear){
x = q[front++];
v[x] = 0;
for (i=head[x]; i!=-1; i=e[i].next){
k = e[i].to;
if (e[i].flow && dis[x]+e[i].cost<dis[k]){
dis[k] = dis[x] + e[i].cost;
pre[k] = i;
if (!v[k]){
v[k] = 1;
q[rear++] = k;
}
}
}
}
if (dis[sink] != INF)
return true;
return false;
}
int costflow(int src, int sink, int n){
int flow = 0, cflow = 0, minf = INF, u;
while (spfa(src,sink,n)){
for (u=pre[sink]; u!=-1; u=pre[e[u].from])
minf = min(minf,e[u].flow);
for (u=pre[sink]; u!=-1; u=pre[e[u].from]){
e[u].flow -= minf;
e[u^1].flow += minf;
}
flow += minf;
cflow += minf*dis[sink];
}
if (flow != 2)
return -1;
else
return cflow;
}
void init(int n){
tot = 0;
memset(head,-1,sizeof(head));
for (int i=2; i<n; i++){
add(i,i+n,0,1);
}
add(1,n+1,0,3);
add(n,n*2,0,3);
add(0,1,0,2);
add(n*2,n*2+1,0,2);
}
int main(){
int n, m, a, b, w;
int i, j, k;
while (scanf("%d %d",&n,&m)!=EOF){
init(n);
while (m--){
scanf("%d %d %d",&a,&b,&w);
if ((a==1 && b==n) || (a==n&&b==1)){
add(n+1,n,w,3);
}
else {
add(a+n,b,w,1);
add(b+n,a,w,1);
}
}
int res = costflow(0,2*n+1, 2*n+1);
if (res == -1){
printf("Mission Failed!\n");
}
else printf("%d\n",res);
}
return 0;
}