1. 程式人生 > >POJ 2135 Farm Tour (最小費用流)

POJ 2135 Farm Tour (最小費用流)

Description

When FJ’s friends visit him on the farm, he likes to show them around. His farm comprises N (1 <= N <= 1000) fields numbered 1..N, the first of which contains his house and the Nth of which contains the big barn. A total M (1 <= M <= 10000) paths that connect the fields in various ways. Each path connects two different fields and has a nonzero length smaller than 35,000.

To show off his farm in the best way, he walks a tour that starts at his house, potentially travels through some fields, and ends at the barn. Later, he returns (potentially through some fields) back to his house again.

He wants his tour to be as short as possible, however he doesn’t want to walk on any given path more than once. Calculate the shortest tour possible. FJ is sure that some tour exists for any given farm.

Input

Line 1: Two space-separated integers: N and M.

Lines 2..M+1: Three space-separated integers that define a path: The starting field, the end field, and the path’s length.

Output

A single line containing the length of the shortest tour.

Sample Input

4 5
1 2 1
2 3 1
3 4 1
1 3 2
2 4 2

Sample Output

6

題意

FJ有N個農場,M條路,FJ要領朋友遊玩,從1走到N,再回到1,不走重複路,每條路長度不一樣,問最短路長為多少。

思路

最小費用最大流,題目等同於從1到N這些點建立的圖,然後我們找兩條不相交從1到N的路徑,並且路徑長度的和最短。

可以轉化為網路流的問題,我們假設每條邊的費用為原來路徑的長度,邊的容量為1,然後給圖中增加一個源點與一個匯點,源點連線圖中第1個點,邊費用為0,容量為2,匯點與第N個點相連,邊費用為0,容量為2。

仔細想想我們可以知道,源點和匯點的加入並不影響總路徑長度,因為與他們相接的邊的長度都為0,但是容量為2代表可以同時從該點出發或匯入的流量增加到原來兩倍,等同於兩條路徑同時有效。

因為原圖邊容量都為1,所以不存在使用網路流計算時會出現重邊情況。

AC程式碼

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<vector>
#include<queue>
using namespace std;

const int MAXX = 40010;
const int INF = 0x3f3f3f3f;

struct edge
{
    int to;
    int next;
    int cap;
    int flow;
    int cost;
} edge[MAXX];

int head[MAXX],tol;
int pre[MAXX],dis[MAXX];
bool vis[MAXX];
int N;  //節點總個數

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

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

bool spfa(int s,int t)
{
    queue<int>q;
    for(int i=0; i<N; i++)
    {
        dis[i]=INF;
        vis[i]=false;
        pre[i]=-1;
    }
    dis[s]=0;
    vis[s]=true;
    q.push(s);
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        vis[u]=false;
        for(int i=head[u]; i!=-1; i=edge[i].next)
        {
            int v=edge[i].to;
            if(edge[i].cap>edge[i].flow&&dis[v]>dis[u]+edge[i].cost)
            {
                dis[v]=dis[u]+edge[i].cost;
                pre[v]=i;
                if(!vis[v])
                {
                    vis[v]=true;
                    q.push(v);
                }
            }
        }
    }
    if(pre[t]==-1)return false;
    else return true;
}

/*
 * int s 起點
 * int t 終點
 * 返回最大流
 */
int minCostMaxFlow(int s,int t)
{
    int cost=0;
    while(spfa(s,t))
    {
        int minn=INF;
        for(int i=pre[t]; i!=-1; i=pre[edge[i^1].to])
        {
            if(minn>edge[i].cap-edge[i].flow)
                minn=edge[i].cap-edge[i].flow;
        }
        for(int i=pre[t]; i!=-1; i=pre[edge[i^1].to])
        {
            edge[i].flow+=minn;
            edge[i^1].flow-=minn;
            cost+=edge[i].cost*minn;
        }
    }
    return cost;
}

int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        init(n+2);  //因為有增加的兩個點
        int st,ed,cost;
        for(int i=0; i<m; i++)
        {
            scanf("%d%d%d",&st,&ed,&cost);
            addedge(st,ed,1,cost);
            addedge(ed,st,1,cost);
        }
        addedge(0,1,2,0);   //增加的源點與匯點
        addedge(n,n+1,2,0);
        printf("%d\n",minCostMaxFlow(0,n+1));
    }
    return 0;
}