POJ 2135:Farm Tour

/**
题目来源:http://poj.org/problem?id=2135

题目翻译:FJ 的农场有N个地方,它的房子在位置1,在位置N有个大barn,FJ要带它的朋友
参观自己的农场,从1到N再从N到1,但是去和回来不能走同一段路,求去和回来的最短路

解题思路:题目可以用流量为2的最小费用流来解决,每条边的容量都设定为1,因为这样经
过一次增广后,被增广的边容量都会变成0.边的容量一旦变成0,就不能走了,刚好符合题目要求
再增广的时候,我们按照路径的花费最少的原则进行增广。
*/
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <queue>
#include <algorithm>

using namespace std;

typedef long long LL;
const int maxn = 50000;
const  LL inf = 4*1e9;
int N,M,S,T;   ///N块地,M条边,S超级源点,T超级汇点
int head[maxn],cnt;
int vis[maxn],maxFlow,pre[maxn];
LL dist[maxn],minFlow,minCostMaxFlow;
struct Edge {
    int from;  ///起点
    int to;    ///终点
    int cap;   ///容量
    LL cost;  ///花费
    int nex;   ///下条边的位置
}edge[maxn];
///加边函数
void addEdge(int u,int v,LL cost) {
    edge[cnt].from = u;
    edge[cnt].to = v;
    edge[cnt].cap = 1;
    edge[cnt].cost = cost;
    edge[cnt].nex = head[u];
    head[u] = cnt++;

    edge[cnt].from = v;
    edge[cnt].to = u;
    edge[cnt].cap = 0;
    edge[cnt].cost = -cost;
    edge[cnt].nex = head[v];
    head[v] = cnt++;
}
///寻找最短增广路
bool spfa() {
    memset(vis,0,sizeof(vis));
    memset(pre,-1,sizeof(pre));
    for(int i = S; i <= T; i++) {
        dist[i] = inf;
    }
    dist[S] = 0;
    vis[S] = 1;
    queue<int>qu;
    qu.push(S);
    while(!qu.empty()) {
        int u = qu.front();
        qu.pop();
        vis[u] = 0;
        for(int i = head[u]; i != -1; i = edge[i].nex) {
            int v = edge[i].to;
            if(edge[i].cap>0 && dist[u]+edge[i].cost<dist[v]) {
                dist[v] = dist[u] + edge[i].cost;
                pre[v] = i;
                if(vis[v] == 0) {
                    vis[v] = 1;
                    qu.push(v);
                }
            }
        }
    }
    if(dist[T] < inf) return true;
    else return false;
}
void min_cost_max_flow() {
    maxFlow = 0;
    minCostMaxFlow = 0;
    while(spfa()) {
        minFlow = inf;
        ///这一部分可以不要,因为知道流量是1
        for(int i = pre[T]; i != -1; i = pre[edge[i].from]) {
            minFlow = min(minFlow,(LL)edge[i].cap);
        }
        for(int i = pre[T]; i != -1; i = pre[edge[i].from]){
            edge[i].cap -= minFlow;
            edge[i^1].cap += minFlow;
        }
        minCostMaxFlow += dist[T]*minFlow;
        maxFlow += minFlow;
        if(maxFlow == 2)
            break;
    }
}
int main() {
    int u,v;
    LL dis;
    while(~scanf("%d%d",&N,&M)) {
        S = 1;
        T = N;
        memset(head,-1,sizeof(head));
        cnt = 0;
        for(int i = 1; i <= M; i++) {
            scanf("%d%d%lld",&u,&v,&dis);
            addEdge(u,v,dis);  
            addEdge(v,u,dis);
        }
        min_cost_max_flow();
        printf("%lld\n",minCostMaxFlow);
    }
    return 0;
}

题目
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值