POJ 2135 Farm Tour 农场之旅


Farm Tour
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 9973 Accepted: 3678

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

Source

最小费用最大流重要有感觉了。
题意是说有n 个农场,标号从1到n,有m条无向路连接这些农场。现在约翰要从1号农场到n号农场,然后返回1号农场。他希望整个路程最短,并且希望不经过同一条路两次。
让你求最短路径。
如果直接做两次最短路的话就错了。单源最短路径问题其实是最小费用最大流问题的一种特殊情况,把每条边的容量改为1,并且设置超级源点连向起点,容量为1,费用为0,超级汇点连接终点,容量为inf,费用为0,做一次最小费用最大流得到的费用便是最短路径。而此题求的是走一个来回,所以可以理解为从起点到终点走两次,且路径不同。那么此题就可以将上面求单源最短路径的方法中的超级源点到起点的流量从1改为2即可。另外此图是无向图,那么建立边的时候,需要将两个方向都读入图中。

#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<cstdio>
using namespace std;
const int MAXN=610*610*2+2;
const int inf=1<<29;
int pre[MAXN];          // pre[v] = k:在增广路上,到达点v的边的编号为k
int dis[MAXN];          // dis[u] = d:从起点s到点u的路径长为d
int vis[MAXN];         // inq[u]:点u是否在队列中
int path[MAXN];
int head[MAXN];
int NE,tot,ans,max_flow,map[666][666];
struct node
{
    int u,v,cap,cost,next;
} Edge[MAXN<<2];
void addEdge(int u,int v,int cap,int cost)
{
    Edge[NE].u=u;
    Edge[NE].v=v;
    Edge[NE].cap=cap;
    Edge[NE].cost=cost;
    Edge[NE].next=head[u];
    head[u]=NE++;
    Edge[NE].v=u;
    Edge[NE].u=v;
    Edge[NE].cap=0;
    Edge[NE].cost=-cost;
    Edge[NE].next=head[v];
    head[v]=NE++;
}
int SPFA(int s,int t)                   //  源点为0,汇点为sink。
{
    int i;
    for(i=s;i<=t;i++) dis[i]=inf;
    memset(vis,0,sizeof(vis));
    memset(pre,-1,sizeof(pre));
    dis[s] = 0;
    queue<int>q;
    q.push(s);
    vis[s] =1;
 while(!q.empty())        //  这里最好用队列,有广搜的意思,堆栈像深搜。
    {
        int u =q.front();
        q.pop();
        for(i=head[u]; i!=-1;i=Edge[i].next)
        {
            int v=Edge[i].v;
            if(Edge[i].cap >0&& dis[v]>dis[u]+Edge[i].cost)
            {
                dis[v] = dis[u] + Edge[i].cost;
                pre[v] = u;
                path[v]=i;
                if(!vis[v])
                {
                    vis[v] =1;
                    q.push(v);
                }
            }
        }
        vis[u] =0;
    }
    if(pre[t]==-1)
        return 0;
    return 1;
}
void end(int s,int t)
{
    int u, sum = inf;
    for(u=t; u!=s; u=pre[u])
    {
        sum = min(sum,Edge[path[u]].cap);
    }
    max_flow+=sum;                          //记录最大流
    for(u = t; u != s; u=pre[u])
    {
        Edge[path[u]].cap -= sum;
        Edge[path[u]^1].cap += sum;
        ans += sum*Edge[path[u]].cost;     //  cost记录的为单位流量费用,必须得乘以流量。
    }
}
int main()
{
    int i,j,n,s,t,m;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        memset(head,-1,sizeof(head));
        NE=ans=max_flow=s=0;
        int u,v,c;
        while(m--)
        {
            scanf("%d%d%d",&u,&v,&c);
            addEdge(u,v,1,c);
            addEdge(v,u,1,c);
        }
        t=n+1;
        addEdge(s,1,2,0);
        addEdge(n,t,inf,0);
        while(SPFA(s,t))
        {
            end(s,t);
        }
        printf("%d\n",ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值