用时:70min
题目大意:给定(N)个点,(M)条边的无向图,每条边只能走一次,求(1 ightarrow N ightarrow 1)的最短路径长度。
居然是费用流!
建图:
- 超级源点(s)和超级汇点(t)。
- (s
ightarrow 1, N
ightarrow t),容量为(2),费用为(0);
即,相当于(1 ightarrow N)走两次。 - 原图中的边,容量为(1),费用为长度;
即,每条边只能走一次。
注意:双向边。
code
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<queue>
#define MogeKo qwq
using namespace std;
const int maxn = 1e5+10;
const int INF = 0x3f3f3f3f;
int n,m,x,y,z,s,t,ans,cnt;
int head[maxn],to[maxn],nxt[maxn],w[maxn],co[maxn];
int fa[maxn],path[maxn],dis[maxn],fl[maxn];
bool vis[maxn];
void add(int x,int y,int W,int C){
to[++cnt] = y;
nxt[cnt] = head[x];
head[x] = cnt;
w[cnt] = W;
co[cnt] = C;
}
void addedge(int x,int y,int W,int C){
add(x,y,W,C);
add(y,x,0,-C);
}
bool SPFA() {
memset(dis,INF,sizeof(dis));
memset(fl,INF,sizeof(fl));
queue <int> q;
dis[s] = 0;
vis[s] = true;
q.push(s);
while(!q.empty()) {
int u = q.front();
q.pop();
vis[u] = false;
for(int i = head[u]; i; i = nxt[i]) {
int v = to[i];
if(dis[v] > dis[u] + co[i] && w[i]) {
fa[v] = u;
path[v] = i;
dis[v] = dis[u] + co[i];
fl[v] = min(fl[u],w[i]);
if(!vis[v]) {
vis[v] = true;
q.push(v);
}
}
}
}
return (dis[t] != INF);
}
void mcmf() {
while(SPFA()) {
for(int i = t; i != s; i = fa[i]) {
int p = path[i];
w[p] -= fl[t];
w[p^1] += fl[t];
}
ans += fl[t]*dis[t];
}
}
int main() {
scanf("%d%d",&n,&m);
s = 0, t = n+1, cnt = 1;
addedge(s,1,2,0);
addedge(n,t,2,0);
for(int i = 1; i <= m; i++) {
scanf("%d%d%d",&x,&y,&z);
addedge(x,y,1,z);
addedge(y,x,1,z);
}
mcmf();
printf("%d",ans);
return 0;
}