UVA 1658 - 海軍上將 (最小費用流+拆點)
阿新 • • 發佈:2018-11-09
題目連結:https://vjudge.net/problem/UVA-1658
思路:拆點+最小費用流。這裡的拆點法是解決網路流限制點的常用辦法,因為如果直接刪除點,會影響很多邊,所以對每個非源點匯點的節點,將其拆成兩個節點,一個真節點,一個假節點,並在節點中建邊,容量為1,如果這條邊滿載了,說明這個真節點被訪問了,就可以通過限制邊來限制點了,所以最後再跑一遍從1到n的流量為2的最小費用流就行。還是比較經典的一道題,值得回味。
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <vector> #include <queue> using namespace std; const int maxn=3*1000+5; const int maxm=10010; const int INF=0x3f3f3f3f; typedef long long ll; struct Edge { int from,to,cap,flow,cost; Edge(int u,int v,int c,int f,int cost):from(u),to(v),cap(c),flow(f),cost(cost){} }; //最小費用流 struct MCMF { vector<int>G[maxn]; vector<Edge>edges; int inq[maxn];//在佇列中 int d[maxn];//bellman—Ford int p[maxn];//上一條弧 int a[maxn];//可改進量 void init(int n) { for(int i=1;i<=n;i++)G[i].clear(); edges.clear(); } void add_edges(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 bellmanFord(int s,int t,int &flow,ll &cost) { for(int i=0;i<maxn;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]=0; 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]) { Q.push(e.to); inq[e.to]=1; } } } } if(d[t]==INF)return false;//s-t不連通 flow+=a[t]; cost+=(ll)d[t]*(ll)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; } //主過程 int mincost(int s,int t,ll &cost) { int flow=0; cost=0; while(bellmanFord(s,t,flow,cost)); return flow; } }; MCMF mcmf; int main() { int n,m; while(scanf("%d%d",&n,&m)!=EOF) { if(n==0)break; mcmf.init(n*2); for(int i=2;i<=n-1;i++) mcmf.add_edges(i,i+n,1,0); mcmf.add_edges(1,n+1,2,0); mcmf.add_edges(n,n*2,2,0); for(int i=0;i<m;i++) { int u,v,cost; scanf("%d%d%d",&u,&v,&cost); mcmf.add_edges(u+n,v,1,cost);//用假節點連線真節點 } ll ans; mcmf.mincost(1,2*n,ans); printf("%lld\n",ans); } return 0; }