pku2135 最小费用最大流 EK算法 邻接表实现

Farm Tour

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

转载于:https://www.cnblogs.com/ylfdrib/archive/2010/08/05/1792735.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值