HITOJ 2739 The Chinese Postman Problem(有向带权图上的中国邮递员问题)

版权声明:转载请注明出处:http://blog.csdn.net/yasola,谢谢 https://blog.csdn.net/Yasola/article/details/77847245

The Chinese Postman Problem
Source : bin3
Time limit : 1 sec Memory limit : 64 M
Submitted : 530, Accepted : 177
A Chinese postman is assigned to a small town in China to deliver letters. In this town, each street is oriented and connects exactly two junctions. The postman’s task is to start at the post office and pass each street at least once to deliver letters. At last, he must return to the post office.
Can you help him to make sure whether there exist feasible routes for him and find the minimum distance from all the feasible routes.
Input
Input contains multiple test cases. The first line is an integer T, the number of test cases. Each case begins with two integers N, M, with 2 ≤ N ≤ 100, 1 ≤ M ≤ 2000, representing the number of junctions and the number of streets respectively.
Then M lines will follow, each denoting a street. A street is represented by three integers u, v, d, with 0 ≤ u, v < N, 0 < d ≤ 1000, meaning this street whose length is d connects the junction u and v and the postman can only travel from junction u to v. Junctions are numbered from 0 to N-1. Junction 0 is always the post office. Note that there may be more than one street connecting the same pair of junctions.
Output
Output one line for each test case. If there exist feasible routes for the postman, output the minimum distance. Otherwise, output -1.
Sample Input
3
2 1
0 1 3
4 4
0 1 1
1 2 2
2 3 3
3 0 4
4 7
0 1 1
1 2 2
2 3 3
3 0 4
1 3 5
3 1 2
1 3 2
Sample Output
-1
10
21

题目大意:

  给你一个有向带边权图,求从任意点出发进过所有边之后回到这个点的最小费用(边可以走多次)。

解题思路:

  首先可以知道,如果存在入度或出度为0的点则无解。并且,如果不连通也无解。
  假设我们先经过每条边一遍,则入度不等于出度的点的流量就出现了不平衡,为了使流量平衡,我们不断添加从出度大于入度到入度大于出度的路径直到平衡为止。
  可以通过最小费用最大流实现这个过程,并使得花费最小。令D(i)i号结点的入度减出度,如果D(i)>0则连一条i到汇点容量为D(i)费用为0的边,如果D(i)<0则连一条源点到i容量为D(i)费用为0的边。对于原图中的每条边起点终点不变,容量为费用为原费用。跑一遍最小费用最大流再加上原图所有边的权就是答案。

AC代码

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <vector>
#include <queue>
#include <cstdlib>
#include <cmath>
#include <string>
#include <map>
using namespace std;
#define INF 0x3f3f3f3f
#define LL long long
#define fi first
#define se second
#define mem(a,b) memset((a),(b),sizeof(a))

const int MAXV=100+3;
const int MAXE=2000+3;

struct Edge
{
    int to,next,cap,cost;
    Edge(int t=0,int n=0,int ca=0,int f=0,int co=0):to(t),next(n),cap(ca),cost(co){}
}edge[2*(MAXE+MAXV)];

int V, E;
int in[MAXV], out[MAXV];
int head[MAXV],tol;
int pre[MAXV],dis[MAXV];
bool vis[MAXV];

void add_edge(int u,int v,int cap,int cost)
{
    edge[tol]=Edge(v,head[u],cap,0,cost);
    head[u]=tol++;
    edge[tol]=Edge(u,head[v],0,0,-cost);
    head[v]=tol++;
}

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

int min_cost_flow(int s,int t,int &cost)//返回的是最大流,cost存的是最小费用
{
    int flow=0;
    cost=0;
    while(spfa(s,t))
    {
        //如果流量任意,当spfa跑出的dis[t]>=0时跳出循环即可
        int the_min=INF;
        for(int i=pre[t];i!=-1;i=pre[edge[i^1].to])
            the_min=min(the_min, edge[i].cap);
        for(int i=pre[t];i!=-1;i=pre[edge[i^1].to])
        {
            edge[i].cap-=the_min;
            edge[i^1].cap+=the_min;
            cost+=edge[i].cost*the_min;
        }
        flow+=the_min;
    }
    return flow;
}

int par[MAXV], high[MAXV];

int findfather(int x)
{
    return par[x]=par[x]==x?x:findfather(par[x]);
}

bool unite(int a, int b)
{
    int fa=findfather(a), fb=findfather(b);
    if(fa==fb)
        return false;
    if(high[fa]>high[fb])
        par[fb]=fa;
    else
    {
        par[fa]=fb;
        if(high[fa]==high[fb])
            ++high[fb];
    }
    return true;
}

void init()
{
    for(int i=0;i<V+2;++i)
    {
        head[i]=-1;
        par[i]=i;
        high[i]=0;
        in[i]=out[i]=0;
    }
    tol=0;
}

int main()
{
    int T_T;
    scanf("%d", &T_T);
    while(T_T--)
    {
        scanf("%d%d", &V, &E);
        init();
        int sum=0;
        for(int i=0;i<E;++i)
        {
            int u, v, c;
            scanf("%d%d%d", &u, &v, &c);
            unite(u, v);
            ++out[u];
            ++in[v];
            add_edge(u, v, INF, c);
            sum+=c;
        }
        bool ok=true;
        for(int i=1;i<V;++i)
            if(findfather(0)!=findfather(i))//如果不连通,则无解
            {
                ok=false;
                break;
            }
        int s=V, t=V+1;
        for(int i=0;i<V;++i)
        {
            if(in[i]==0 || out[i]==0)//存在入度或出度为0的点,无解
            {
                ok=false;
                break;
            }
            if(out[i]-in[i]>0)
                add_edge(i, t, out[i]-in[i], 0);
            else if(out[i]-in[i]<0)
                add_edge(s, i, in[i]-out[i], 0);
        }
        if(!ok)
        {
            puts("-1");
            continue;
        }
        V+=2;
        int ans;
        min_cost_flow(s, t, ans);
        printf("%d\n", ans+sum);
    }

    return 0;
}
展开阅读全文

没有更多推荐了,返回首页