题意:FJ带他的朋友参观农场,他们的起点是1,终点是N, 点与点之间可能存在路径。现在要求从1到N,在从N回到1所走的最短路程。并且返回时已经被走过的路不能再走!
题解:最小费用流。每条路只走一次,说明每一条边的容量为1。由于是无向图,所以从N走回1的过程其实就等同于再从1到N走一遍。那么就需要寻找两条从1到N的最短路径。取一个超级源点,将其与1连接,边的容量为2(两条路),费用为0。N与一超级汇点连接,容量也为2,费用为0。还有加边的时候需要特别注意!!!
例如下面的图:
倘若输入的是这样一组数据:
4 5
1 2 10
1 3 2
2 4 4
2 3 1
3 4 10
在只加单向边的情况下。那么的到得第一题增广路径将会是1->3->4。 而不是1->3->2->4
#include <iostream> using namespace std; #define MAX 30000 #define INF 999999999 #define min(a,b) (a<b?a:b) struct Edge { int st, ed; int cost, flow; int next; } edge[MAX*2]; int dis[MAX]; int pre[MAX]; int head[MAX]; int que[MAX]; bool mark[MAX]; int E, src, dest; void add_edge ( int u, int v, int flw, int cst ) { edge[E].st = u; edge[E].ed = v; edge[E].flow = flw; edge[E].cost = cst; edge[E].next = head[u]; head[u] = E++; edge[E].st = v; edge[E].ed = u; edge[E].flow = 0; /* 一开始残留网络为0,故这条反向边容量为0。(它仅仅是用来表示残留网络的!) */ /* 想一想,图是无向图,从u到v的容量是flw, 那么自然的,从v到u的初始容量也应该是flw !。所以还需要建一条边(v,u)。 */ edge[E].cost = -cst; edge[E].next = head[v]; head[v] = E++; } void spfa () { int u, v, i, front, rear; memset(mark,0,sizeof(mark)); memset(pre,-1,sizeof(pre)); for ( i = src; i <= dest; i++ ) dis[i] = INF; front = rear = 0; que[rear++] = src; dis[src] = 0; mark[src] = true; while ( front != ( rear + 1 ) % MAX ) { u = que[front]; front = ( front + 1 ) % MAX; mark[u] = false; for ( i = head[u]; i != -1; i = edge[i].next ) { v = edge[i].ed; if ( edge[i].flow > 0 && dis[v] > dis[u] + edge[i].cost ) { dis[v] = dis[u] + edge[i].cost; pre[v] = i; if ( ! mark[v] ) { mark[v] = true; que[rear] = v; rear = ( rear + 1 ) % MAX; } } } } } int mcmf () { int res = 0; while ( 1 ) { spfa (); if ( dis[dest] == INF ) break; int u = dest, minf = INF; while ( u != src ) { minf = min ( edge[pre[u]].flow, minf ); u = edge[pre[u]].st; } u = dest; while ( u != src ) { edge[pre[u]].flow -= minf; edge[pre[u]^1].flow += minf; res += edge[pre[u]].cost * minf; u = edge[pre[u]].st; } } return res; } int main() { int u, v, len, N, M; scanf("%d%d",&N,&M); E = src = 0; dest = N + 1; memset(head,-1,sizeof(head)); while ( M-- ) { scanf("%d%d%d",&u,&v,&len); add_edge ( u, v, 1, len ); add_edge ( v, u, 1, len ); // 卡死在这里了···。 } add_edge ( src, 1, 2, 0 ); add_edge ( N, dest, 2, 0 ); printf("%d\n",mcmf()); system("pause"); return 0; }