1. 程式人生 > >2017烏魯木齊網絡賽 J題 Our Journey of Dalian Ends ( 最小費用最大流 )

2017烏魯木齊網絡賽 J題 Our Journey of Dalian Ends ( 最小費用最大流 )

增廣路 ali += ase turn src eof weight flow

題目鏈接

題意 : 給出一副圖,大連是起點,終點是西安,要求你求出從起點到終點且經過中轉點上海的最小花費是多少?

分析 :

最短路是最小費用最大流的一個特例,所以有些包含中轉限制或者經過點次數有限制的最短路問題都可以考慮使用最小費用最大流來建圖解決。

首先對於每個點都只能經過一次這個限制,在網絡流中是比較常見的一個限制,只要將所有的點由一拆二且兩點間連容量為 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; }
View Code

2017烏魯木齊網絡賽 J題 Our Journey of Dalian Ends ( 最小費用最大流 )