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));
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章