题意简单: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; }