2017乌鲁木齐网络赛 J题 Our Journey of Dalian Ends ( 最小费用最大流 )
题意 : 给出一副图,大连是起点,终点是西安,要求你求出从起点到终点且经过中转点上海的最小花费是多少?
分析 :
最短路是最小费用最大流的一个特例,所以有些包含中转限制或者经过点次数有限制的最短路问题都可以考虑使用最小费用最大流来建图解决。
首先对于每个点都只能经过一次这个限制,在网络流中是比较常见的一个限制,只要将所有的点由一拆二且两点间连容量为 1 且花费为 0 的边。
这题的建图很巧妙,是将中转点作为汇点,提示到了这里不如停下来想想如何建图?
然后抽象出一个超级源点,然后将起点和终点与超级源点连一条容量为 1 且 花费为 0 的边,最后将上海这个中转点作为超级汇点。
最后跑出的最小费用最大流就是答案,当然最大流应当是要等于 2 的,如果没有解则说明 MaxFlow < 2。
#include<bits/stdc++.h> using namespace std; const int maxn = 1e4 + 10; const int INF = 0x3f3f3f3f; struct st{ int from, to, w; }arr[10010]; map<string, int> mp; int id; struct Edge { int from,to,cap,flow,cost; Edge(int u,int v,int ca,int f,int co):from(u),to(v),cap(ca),flow(f),cost(co){}; }; struct MCMF { int n,m,s,t; vector<Edge> edges; vector<int> G[maxn]; int inq[maxn];//是否在队列中 int d[maxn];//距离 int p[maxn];//上一条弧 int a[maxn];//可改进量 void init(int n)//初始化 { this->n=n; for(int i=0;i<=n;i++) G[i].clear(); edges.clear(); } void AddEdge(int from,int to,int cap,int cost)//加边 { edges.push_back(Edge(from,to,cap,0,cost)); edges.push_back(Edge(to,from,0,0,-cost)); int m=edges.size(); G[from].push_back(m-2); G[to].push_back(m-1); } bool SPFA(int s,int t,int &flow,int &cost)//寻找最小费用的增广路,使用引用同时修改原flow,cost { for(int i=0;i<n;i++) d[i]=INF; memset(inq,0,sizeof(inq)); d[s]=0;inq[s]=1;p[s]=0;a[s]=INF; queue<int> Q; Q.push(s); while(!Q.empty()) { int u=Q.front(); Q.pop(); inq[u]--; for(int i=0;i<G[u].size();i++) { Edge& e=edges[G[u][i]]; if(e.cap>e.flow && d[e.to]>d[u]+e.cost)//满足可增广且可变短 { d[e.to]=d[u]+e.cost; p[e.to]=G[u][i]; a[e.to]=min(a[u],e.cap-e.flow); if(!inq[e.to]) { inq[e.to]++; Q.push(e.to); } } } } if(d[t]==INF) return false;//汇点不可达则退出 flow+=a[t]; cost+=d[t]*a[t]; int u=t; while(u!=s)//更新正向边和反向边 { edges[p[u]].flow+=a[t]; edges[p[u]^1].flow-=a[t]; u=edges[p[u]].from; } return true; } int MincotMaxflow(int s,int t) { int flow=0,cost=0; while(SPFA(s,t,flow,cost)); return cost; } }MM; inline void init() { mp.clear(); mp["Shanghai"] = 1;///中转点上海 mp["Dalian"] = 2;///起点大连 mp["Xian"] = 3;///终点西安 id = 4; } int main(void) { int nCase; cin>>nCase; while(nCase--){ init(); int M; cin>>M; string From, To; int Weight; for(int i=1; i<=M; i++){ cin>>From>>To>>Weight; if(!mp.count(From)) mp[From] = id++; if(!mp.count(To)) mp[To] = id++; arr[i].from = mp[From]; arr[i].to = mp[To]; arr[i].w = Weight; } int n = id-1; MM.init(2*n+1); MM.AddEdge(0, 2, 1, 0); MM.AddEdge(0, 3, 1, 0); for(int i=1; i<=n; i++) MM.AddEdge(i, i+n, 1, 0); for(int i=1; i<=M; i++){ MM.AddEdge(arr[i].from+n, arr[i].to, 1, arr[i].w); MM.AddEdge(arr[i].to+n, arr[i].from, 1, arr[i].w); } printf("%d\n", MM.MincotMaxflow(0, 1)); } return 0; }