例题11-9 海军上将(Admiral, ACM/ICPC NWERC 2012, UVa1658)

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

题目描述

例题11-9 海军上将(Admiral, ACM/ICPC NWERC 2012, UVa1658)_第1张图片

题意解析

给出一个v(3≤v≤1000)个点e(3≤e≤10000)条边的有向加权图,求1~v的两条不相交(除了起点和终点外没有公共点)的路径,使得权和最小。

算法设计

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

把2到v-1的每个结点i拆成i和i’两个结点,中间连一条容量为1,费用为0的边,然后求1到v的流量为2的最小费用流即可。

我在这个提示的基础上做出了一些改善,我把1~v所有的点都拆成了两个点,例如对编号为i的点拆成了ii+v两个点,连边的方法是:

  1. 1~1+vv~v+v之间连一条容量为2、费用为0的边,表示这两个点最多可以经过2次
  2. 2v-1的每个结点i拆成i和i+v两个结点,中间连一条容量为1,费用为0的边,表示这些点最多可以经过1次
  3. 把题目中出现的由a向b,权值为c的边,表示为由a+vb,容量为1,费用为c的边,表示这条边最多可以经过1次

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

注意点

每个测试点有多个测试样例

C++代码

#include
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=2005;//无穷大,结点最大个数
vector<Edge>edges;
vector<vector<int>>graph(MAXV);
int v,e;
void insertEdge(int id1,int id2,int cap,int cost){//插入边
    graph[id1].push_back(edges.size());
    edges.push_back(Edge(id1,id2,cap,0,cost));
    graph[id2].push_back(edges.size());
    edges.push_back(Edge(id2,id1,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(){
    while(~scanf("%d%d",&v,&e)&&v!=0){
        edges.clear();
        fill(graph.begin(),graph.end(),vector<int>());
        insertEdge(1,1+v,2,0);
        for(int i=2;i<v;++i)
            insertEdge(i,i+v,1,0);
        insertEdge(v,v+v,2,0);
        for(int i=0;i<e;++i){
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            insertEdge(a+v,b,1,c);
        }
        auto i=MinCostMaxFlow(1,v+v);
        printf("%lld\n",i.second);
    }
    return 0;
}

你可能感兴趣的:(算法竞赛入门经典,-,Uva)