题意简单:FJ有N个农场,M条路,FJ要领朋友游玩,从1走到N,再回到1,不走重复路,每条路长度不一样,问最短路长为多少。
转化为最小费用流来求解,建一源点S,指向节点1,容量为2,距离0,建一汇点T,N节点指向汇点,容量为2,距离为0,表示要有两条路从S到T,其余给定的路,容量为1,边权为路长,表示每条路只走一次。
刚学的最小费用流,用邻接表实现了一下:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define MM 40010
#define NN 1010
#define INF 0xfffffff
typedef struct node{
int v, f, b;
struct node *nxt;
}NODE;
NODE edg[MM]; // 存边
NODE *link[NN]; // i节点形成的链表的首节点,也表示与i节点相邻的第一个节点
int N, M, edNum, S, T;
int dis[NN];
int mark[NN];
int stack[NN];
int pre[NN]; // 记录最短路径中i节点的前驱顶点
int preEdg[NN]; // 记录最短路径中以i节点为终点的边
int Min(int a, int b){
return a < b ? a : b;
}
void add(int u, int v, int b, int f){// 用邻接表存
edg[edNum].v = v;
edg[edNum].f = f;
edg[edNum].b = b;
edg[edNum].nxt = link[u];
link[u] = edg + edNum++;
}
int Spfa(){
int top, u, v, i;
for (i = 0; i <= N + 1; i++){
mark[i] = 0;
dis[i] = INF;
}
dis[0] = 0;
stack[1] = S;
mark[S] = 1;
top = 1;
while(top){
u = stack[top--];
mark[u] = 0; // 记得置零
for (NODE *p = link[u]; p; p = p->nxt){
v = p->v;
if (p->f && dis[v] > dis[u] + p->b){
dis[v] = dis[u] + p->b;
pre[v] = u;
preEdg[v] = p - edg;
if (!mark[v]){
mark[v] = 1;
stack[++top] = v;
}
}
}
}
return dis[T];
}
int MinCostMaxFlow()
{
int c, t, e, rev, minf, ans = 0;
while((c = Spfa()) != INF){
minf = INF;
t = T;
while(t != S){ // 求得最短路径中的最小流值的增量
e = preEdg[t];
minf = Min(minf, edg[e].f);
t = pre[t];
}
t = T;
while(t != S){
e = preEdg[t];
edg[e].f -= minf;
rev = e + 1 + (-2) * (e % 2); // 求反向边
edg[rev].f += minf;
edg[rev].b = -edg[e].b;
t = pre[t];
}
ans += minf * c;
}
return ans;
}
int main()
{
int u, v, c;
scanf("%d%d", &N, &M);
memset(link, 0, sizeof(link));
while(M--){
scanf("%d%d%d", &u, &v, &c);
add(u, v, c, 1);
add(v, u, INF, 0);
add(v, u, c, 1);
add(u, v, INF, 0);
}
S = 0; // 源点
T = N + 1; // 汇点
add(S, 1, 0, 2);
add(1, S, INF, 0);
add(N, T, 0, 2);
add(T, N, INF, 0);
printf("%d\n", MinCostMaxFlow());
// system("pause");
return 0;
}