1. 程式人生 > >poj 2135 Farm Tour (最小費用最大流模板)

poj 2135 Farm Tour (最小費用最大流模板)

網路流的費用: 在實際應用中,與網路流有關的問題,不僅涉及流量,而且還有費用的因素。網路的每一條邊(v,w)除了給定容量cap(v,w)外,還定義了一個單位流量費用cost(v,w)

最小費用最大流問題 給定網路G,要求G的一個最大用流flow,使流的總費用最小。

求解MCMF問題的演算法: 最小費用最大流最常用和基本的演算法我們可以稱它為最小費用路演算法,其思想與求最大流的增廣路演算法類似,不斷在殘流網路中尋找從源s到匯t的最小費用路,即殘流網路中從s到t的以費用為權的最短路,然後沿最小費用路增流,直至找到最小費用流。

當殘流網路中邊(v,w)是向前邊時,其費用為cost(v,w); 當(v,w)是向後邊時,其費用為-cost(w,v)。


步驟0:初始可行0流。  步驟1:如果不存在最小費用路,則計算結束,已經找到最小費用流;否則用最短路演算法在殘流網路中找從s到t的最小費用可增廣路,轉步驟2。  步驟2:沿找到的最小費用可增廣路增流,並轉步驟1。 

最小費用路演算法的複雜度主要依靠於求最短路的方法,由於負權的存在,不能選擇dijstra等演算法,一般bellman-ford,spfa等用來解決費用流的最短路問題。

Farm Tour
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 11372 Accepted: 4218

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

這道題有兩個值得注意的地方,第一個是建圖,無向邊只允許通過一次,往返的兩條線路可以看成是從源點到匯點的兩條線路,所以只要從附加源點向源點連一條容量為2的邊和從匯點向附加匯點連一條容量為2的邊就可以限線路為兩條.
第一個值得注意的地方是由於有重邊的出現,所以這道題要用鄰接表實現.

<pre name="code" class="cpp">#include"stdio.h"
#include"string.h"
#include"queue"
using namespace std;
#define M 1100
const int inf=0x7fffffff;
struct node
{
    int u,v,c,f,next;  //C為花費,F為flow流量
}e[M*40];
int pre[M],dis[M],head[M],t;
int vis[M];
void add1(int u,int v,int c,int f)
{
    e[t].u=u;
    e[t].v=v;
    e[t].c=c;
    e[t].f=f;
    e[t].next=head[u];
    head[u]=t++;
}
void add(int u,int v,int c,int f)
{
    add1(u,v,c,f);      
    add1(v,u,-c,0);  //反向邊流量初始為零,如果走反向邊費用正好和原邊抵消
}
int spfa(int s,int t)
{
    int i,u,v;
    queue<int>q;
    q.push(s);
    memset(vis,0,sizeof(vis));
    memset(pre,-1,sizeof(pre));
    for(i=s;i<=t;i++)
        dis[i]=inf;
    dis[s]=0;
    while(!q.empty())
    {
        u=q.front();
        q.pop();
        for(i=head[u];i!=-1;i=e[i].next)
        {
            v=e[i].v;
            if(e[i].f&&dis[v]>dis[u]+e[i].c)  //找到一條最小費用流
            {
                dis[v]=dis[u]+e[i].c;  
                pre[v]=i;       //記錄路徑
                if(!vis[v])
                {
                    vis[v]=1;
                    q.push(v);
                }
            }
        }
        vis[u]=0;
    }
    if(dis[t]!=inf)
        return 1;
    return 0;
}
void solve(int s,int t)
{
    int ans=0,i,j;
	int flow=0,cost=0;     //總流量、總費用
    while(spfa(s,t))
    {
		int minf=inf;
		for(i=pre[t];i!=-1;i=pre[e[i].u])
		{
			if(e[i].f<minf)
				minf=e[i].f;
		}
		flow+=minf;   //該條路徑的流量
        for(i=pre[t];i!=-1;i=pre[e[i].u])
        {
            j=i^1;
            e[i].f-=minf;
            e[j].f+=minf;
        }
	cost+=dis[t]*minf;   //單位運費和乘以流量得費用   
    }
    printf("%d\n",cost);
}
int main()
{
    int i,u,v,c,n,m;
    while(scanf("%d%d",&n,&m)!=-1)
    {
        t=0;
        memset(head,-1,sizeof(head));
        for(i=0;i<m;i++)
        {
            scanf("%d%d%d",&u,&v,&c);  //邊長度看做單位運費
            add(u,v,c,1);
            add(v,u,c,1);       //無向邊,費用為長度,流量為1(只能通過一次)
        }
//往返的兩條線路可以看成是從源點到匯點的兩條線路,
//所以只要從附加源點向源點連一條容量為2的邊和從匯點向附加匯點連一條容量為2的邊就可以限線路為兩條.
        add(0,1,0,2);
        add(n,n+1,0,2);
        solve(0,n+1);
    }
    return 0;
}