Farm Tour 最小费用流

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又来搞事,他这次想在无向图里,从节点1到节点N走一圈,问走着一圈最短的距离是多少。
首先呢,先说一下为什么不能两次最短路求解。
Farm Tour  最小费用流对于这个图,大家可以试着自己跑一下最短路和最小费用流。
如果对于一个有向图,那么在找去程和回程的路径时,选择的边不相重复,也就是说这两个过程相互独立,是可以用最短路单独计算去程和回程的最短路并相加的。
但是对于一个无向图,在寻找去程和回程的路径时,是从同一个边集中挑选边,也就是说这两个过程相互关联,无法一次贪心得到结果,所以采用最小费用流模型求解。

最后是关于无向图建边的问题。如果每条边只能用一次的话,是不能正反建两次的,因为这样建边会导致这条边在算法中用过两次;如果没有限制,就正反建两次,只要不超出内存限制的话。
那么有限制的话要真么处理呢?首先看一下这条无向边初始情况,开始时,这条边即可以正向走,也可以反向走
Farm Tour  最小费用流
但是一旦选定了一个方向走,比如从A到B,那么这个方向就不能在选用,而且被选择方向的反方向(B到A)变成负权边,用于表示退流
Farm Tour  最小费用流综上,在建边的时候,初始化正向和反向容量都是1,之后则与普通的最小费用流无异。
如果这样的话,考虑上图退流之后边的状态。
Farm Tour  最小费用流我们惊奇的发现,无法表示从B到A这条路径了,那这样是不是代表这样建边是错的呢?
那不然不是。我们可以想一下,在退流之后这条边端点的状态。在退流之后,这两个端点都被连上另一条新的边,而且 连上新的边所形成的路径 要比之前 使用这条边形成的路径 的权值要小,也就是说,这条边不会再被使用了,所以他能不能表示正确的状态已经无所谓了。


#include <stdio.h>
#include <climits>
#include <cstring>
#include <time.h>
#include <math.h>
#include <iostream>
#include <algorithm>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <utility>
#include <vector>
#include <string>

#define MAXN 1005
#define LIM 3500000000
#define starNode 0
#define INF 0x3f3f3f3f
#define ll long long
#define Pair pair<int,int>
#define re return

#define mem(a,b) memset(a,b,sizeof(a))
#define Make(a,b) make_pair(a,b)
#define getLen(name,index) name[index].size()
#define rep(index,star,finish) for(register int index=star;index<finish;index++)
#define drep(index,finish,star) for(register int index=finish;index>=star;index--)
using namespace std;
struct Edge{
    int to,cap,rev,len;
};

int N,M;
ll dis[MAXN];
int Vpre[MAXN],Epre[MAXN];
vector<Edge> adj[MAXN];
inline void addEdge(int star,int finish,int cap,int len);
int minCost(int star,int finish,int f);
int main(){
    ios::sync_with_stdio(false);

    while(cin>>N>>M){
        rep(i,0,M){
            int commonA,commonB,len;
            cin>>commonA>>commonB>>len;
            addEdge(commonA,commonB,1,len);
        }
        adj[starNode].push_back((Edge){1,2,getLen(adj,1),0});
        adj[starNode].push_back((Edge){starNode,0,getLen(adj,starNode)-1,0});

        cout<<minCost(starNode,N,2)<<endl;

        rep(i,0,N+1)
            adj[i].clear();
    }

    re 0;
}
inline void addEdge(int star,int finish,int cap,int len){
    adj[star].push_back((Edge{finish,cap,getLen(adj,finish),len}));
    adj[finish].push_back((Edge){star,cap,getLen(adj,star)-1,len});
}
int minCost(int star,int finish,int f){
    int sum=0;

    while(f>0){
        rep(i,0,N+1)
            dis[i]=LIM;
        dis[1]=0;
        bool update=true;
        while(update){
            update=false;
            rep(i,1,N+1){
                if(dis[i]==LIM)
                    continue;
                rep(j,0,getLen(adj,i)){
                    Edge &e=adj[i][j];
                    if(e.cap>0 && dis[e.to]>dis[i]+e.len){
                        dis[e.to]=dis[i]+e.len;
                        Vpre[e.to]=i;
                        Epre[e.to]=j;
                        update=true;
                    }
                }

            }
        }
        if(dis[finish]==LIM){
            cout<<"need to finish"<<endl;
            re -1;
        }

        for(int v=finish;v!=star;v=Vpre[v]){
            Edge &e=adj[Vpre[v]][Epre[v]];

            sum+=e.len;
            e.cap=0;
            e.len*=(-1);
            adj[e.to][e.rev].cap=1;
            adj[e.to][e.rev].len*=(-1);
        }

        f--;
    }
    return sum;
}

上一篇:Sightseeing tour 【混合图欧拉回路】


下一篇:linux cp mv mkdir rmdir rm touch