例题11-10 最优巴士路线设计(Optimal Bus Route Design, ACM/ICPC Taiwan 2005, UVa12264)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/richenyunqi/article/details/89636884

欢迎访问我的Uva题解目录哦 https://blog.csdn.net/richenyunqi/article/details/81149109

题目描述

例题11-10 最优巴士路线设计(Optimal Bus Route Design, ACM/ICPC Taiwan 2005, UVa12264)题目描述

题意解析

给n个点(n≤100)的有向带权图,找若干个有向圈,每个点恰好属于一个圈。要求权和尽量小。注意即使(u,v)和(v,u)都存在,它们的权值也不一定相同。

算法设计

参考《算法竞赛入门经典(第2版)》的提示:

每个点恰好属于一个有向圈,意味着每个点都有一个唯一的后继。反过来,只要每个点都有唯一的后继,每个点一定恰好属于一个圈。“每个东西恰好有唯一的……”让我们想到了二分图匹配。把每个点 i i 拆成 X i Xi Y i Yi ,原图中的有向边 u > v u->v 对应二分图中的边 X u > Y v Xu->Yv ,则题目转化为了这个二分图上的最小权完美匹配问题。

假设共有n个点,令s=0为源点,t=2×n+1为汇点,对于1~n这n个点,将编号为i的点拆成了ii+v两个点,连边的原则是:

  1. 在源点s1~n这n个点间各连一条容量为1、费用为0的边;在n+1~n+n这n个点与汇点t之间各连一条容量为1、费用为0的边
  2. 把题目中出现的由ab,权值为c的边,表示为由ab+n,容量为1,费用为c的边,表示这条边最多可以经过1次

然后利用最小费用最大流的算法对这一网络进行求解即可。

C++代码

#include<bits/stdc++.h>
using namespace std;
struct Edge{
    int from,to,cap,flow,cost;
    Edge(int f,int t,int c,int fl,int co):from(f),to(t),cap(c),flow(fl),cost(co){}
};
const int INF=0x3fffffff,MAXV=205;//无穷大,结点最大个数
vector<Edge>edges;
vector<vector<int>>graph(MAXV);
void insertEdge(int from,int to,int cap,int cost){//插入边
    graph[from].push_back(edges.size());
    edges.push_back(Edge(from,to,cap,0,cost));
    graph[to].push_back(edges.size());
    edges.push_back(Edge(to,from,0,0,-cost));
}
bool BellmanFord(int s,int t,int&flow,long long&cost){//最大流算法,s为源点,t为汇点
    int a[MAXV]={0},p[MAXV]={0},dis[MAXV]={0};//a数组表示源点到结点a[i]的残量,p数组表示最短路树上到达结点p[i]的边在edges数组中的序号
    fill(dis,dis+MAXV,INF);
    bool inQueue[MAXV]={false};
    dis[s]=0;
    inQueue[s]=true;
    a[s]=INF;//起点的残量置为无穷大
    queue<int>q;
    q.push(s);
    while(!q.empty()){//广度优先遍历查找从源点到达汇点的增广路
        int u=q.front();
        q.pop();
        inQueue[u]=false;
        for(int i:graph[u]){//遍历以x为起点的边
            Edge&e=edges[i];
            if(e.cap>e.flow&&dis[e.to]>dis[u]+e.cost){//当前边的终点的残量为0且容量大于流量
                dis[e.to]=dis[u]+e.cost;
                p[e.to]=i;//更新到达该终点的边的编号
                a[e.to]=min(a[u],e.cap-e.flow);//更新源点到该终点的残量
                if(!inQueue[e.to]){
                    inQueue[e.to]=true;
                    q.push(e.to);//压入队列
                }
            }
        }
    }
    if(dis[t]==INF)
        return false;
    flow+=a[t];
    cost+=dis[t]*1ll*a[t];
    for(int u=t;u!=s;u=edges[p[u]].from){//从汇点向前遍历增广路经,更新每条增广路的流量
        edges[p[u]].flow+=a[t];
        edges[p[u]^1].flow-=a[t];
    }
    return true;
}
//需要保证初始网络中没有负权环
pair<int,long long> MinCostMaxFlow(int s,int t){
    int flow=0;
    long long cost=0;
    while(BellmanFord(s,t,flow,cost));
    return {flow,cost};
}
int main(){
    int n,a,b;
    while(~scanf("%d",&n)&&n!=0){
        edges.clear();
        fill(graph.begin(),graph.end(),vector<int>());
        int s=0,t=2*n+1;
        for(int i=1;i<=n;++i){
            insertEdge(s,i,1,0);
            insertEdge(i+n,t,1,0);
            while(~scanf("%d",&a)&&a!=0){
                scanf("%d",&b);
                insertEdge(i,a+n,1,b);
            }
        }
        auto i=MinCostMaxFlow(s,t);
        if(i.first==n){
            printf("%d\n",i.second);
        }else
            puts("N");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/richenyunqi/article/details/89636884