POJ 2135 Farm Tour (最小费用最大流)(来回最短路)

  • 题目链接:http://poj.org/problem?id=2135
  • 题意:给你一个无向图,要找出从1号点 到 N号点 的两条不相干的路径的最小总长。
  • 思路:用一个超级源点,连向1号点,长度为0,容量为2。再将N号点连向一个超级汇点,长度为0,容量为2(两条路径)。其他边的话按照原图里的建双向边,cost就是其边长,容量为1(路径之间不相干)。然后直接算最小费用最大流就行了。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <math.h>
#define pi acos(-1)
#define fastio ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
const int INF = 0x3f3f3f3f;
const LL ll_INF = 0x3f3f3f3f3f3f3f3f;
const int maxn = 200 + 10;
const int Max_row = 110, Max_col = 110, Max_v = 110*3, Max_e = 110*110+110*3;

char Grid[Max_row][Max_col];
PII Vertex[Max_v];
int tot=0;


int tol;
int head[Max_e];
int st=0,en=0;
int dis[maxn*maxn];
int vis[maxn*maxn];
int pre[maxn*maxn];//记录增广路径上 到达点i的边的编号

//最小费用最大流模版.求最大费用最大流建图时把费用取负即可。
//无向边转换成有向边时需要拆分成两条有向边。即两次加边。


struct Edge
{
      int u;
      int v;
      int cap;
      int cost;
      int next;
}edge[maxn*maxn + maxn*3];

void init()
{
      memset(head,-1,sizeof(head));
      tol=0;
      tot=0;
}

void add_edge(int u,int v,int cap,int cost)
{
      edge[tol].u=u;edge[tol].v=v;edge[tol].cap=cap;
      edge[tol].cost=cost;edge[tol].next=head[u];
      head[u]=tol++;
      edge[tol].u=v;edge[tol].v=u;edge[tol].cap=0;
      edge[tol].cost=-cost;edge[tol].next=head[v];
      head[v]=tol++;
}

bool spfa(int s,int t ,int n)//0表示没有增广路 //寻找花销最少的路径
{
     //跑一遍SPFA 找s——t的最少花销路径 且该路径上每一条边不能满流
     //若存在 说明可以继续增广,反之不能

      for(int i=0;i<=n;i++)
      {
            dis[i]=INF;
            vis[i]=0;
            pre[i]=-1;
      }
      dis[s]=0;
      vis[s]=1;
      queue<int> Q;
      Q.push(s);
      while(!Q.empty())
      {
            int u=Q.front();
            Q.pop();
            vis[u]=0;
            for(int k=head[u];k!=-1;k=edge[k].next)
            {
                  int v=edge[k].v;
                  int cost=edge[k].cost;
                  if(edge[k].cap && dis[v]> dis[u]+cost)  //可以松弛 且 没有满流
                  {
                        dis[v]=dis[u]+cost;
                        pre[v]=k; //记录前驱边 的编号
                        if(!vis[v])
                        {
                              vis[v]=1;
                              Q.push(v);
                        }
                  }
            }
      }
      if(dis[t]==INF)return 0;
      return 1;
}

int MCMF(int s,int t,int n)
{
      int minflow;//总流量
      int mincost=0;//总费用
      while(spfa(s,t,n))//每次寻找花销最小的路径
      {
            minflow=INF;//通过反向弧 在源点到汇点的最少花费路径 找最小增广流
            for(int k=pre[t];k!=-1;k=pre[edge[k].u])
            {
                  minflow=min(minflow,edge[k].cap);
            }
             //增广
            for(int k=pre[t];k!=-1;k=pre[edge[k].u])
            {
                  edge[k].cap-=minflow;
                  edge[k^1].cap+=minflow;//增广流的花销
            }
            mincost+=dis[t];
      }
      return mincost;
}



int main()
{
    init();
    int N, M;
    scanf("%d%d", &N, &M);
    st = 0;
    en = N + 1;
    for(int i=1; i<=M; i++){
        int u, v, cost;
        scanf("%d%d%d", &u, &v, &cost);
        add_edge(u, v, 1, cost);
        add_edge(v, u, 1, cost);
    }
    add_edge(st, 1, 2, 0);
    add_edge(N, en, 2, 0);
    printf("%d\n", MCMF(st, en, en));
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值