标签:
和UVA - 1658 Admiral如出一撤,就是跑一个流量为2的最小费用流。
主要来学习一下用dijkstra处理负边权来增广,主要思想是每个点都维护一个顶标h[v],叫做v的势。
对于每个边ei(u,v)(u到v的有向边),修正它们的边权w[i] 为w‘[i] = w[i] + h[u] - h[v]。看上去有点奇怪?别急,待会可以看到h的作用。
通过适当取h的值可以保证修正后的边权都非负。取h[x]为当前残留网络下s到u的最短距离, 显然有h[v] ≤ h[u] + w[i],w‘[i] ≥ 0,而且可以
知道只有ei在最短路上,w‘[i] = 0。具体的维护方法可以看代码注释。
复杂度:
用spfa(队列优化的Bellman-Ford)增广的复杂度是O(F*V*E),用dijkstra增广则可以降低到O(F*V^2)或者O(F*E*logV)。
因为实际实现用优先队列,不能修改优先级,且优先队列重载的容器是vector,vector首次扩容是nlogn的。(因此不要把优先队列定义在循环内,pd_ds库里有堆的黑科技)
这题上dijkstra实际运行时间甚至比spfa要长。(只要不是出题人有意卡数据一般没问题,而且费用流大多数人用的是spfa吧。)
code:贴版(大雾)
/********************************************************* * ------------------ * * author AbyssalFish * **********************************************************/ #include<cstdio> #include<iostream> #include<string> #include<cstring> #include<queue> #include<vector> #include<stack> #include<vector> #include<map> #include<set> #include<algorithm> #include<cmath> using namespace std; const int maxn = 2505; template<typename T> struct BinaryHEAP { T Heap[maxn]; int sz; #define Cmp(a,b) ((a)<(b)) //small void push(const T &x) { int i = ++sz; while(i > 1){ int p = i>>1; if(!Cmp(x,Heap[p])) break; Heap[i] = Heap[p]; //如果复杂结构体改成指针 i = p; } Heap[i] = x; } void pop() { T &x = Heap[sz--]; int i = 1; while((i<<1)<=sz){ int a = i<<1, b = i<<1|1; if(b<=sz && Cmp(Heap[b],Heap[a])) a = b; if(!Cmp(Heap[a],x)) break; Heap[i] = Heap[a]; i = a; } Heap[i] = x; } T &operator[](int x){ return Heap[x]; } }; const int maxv = 1000, maxe = 4e4; int hd[maxv],to[maxe],nx[maxe],ec,cap[maxe],cost[maxe]; #define eachEage int i = hd[u]; ~i; i = nx[i] void add(int u,int v,int cp,int cst) { nx[ec] = hd[u]; to[ec] = v; cap[ec] = cp; cost[ec] = cst; hd[u] = ec++; } void Add(int u,int v,int cp,int cst) { add(u,v,cp,cst); add(v,u,0,-cst); } bool inq[maxv]; int dist[maxv], prve[maxv]; int pot[maxv]; //势 const int INF = 0x3f3f3f3f; typedef pair<int,int> hNode; #define fi first #define se second int Ver;//vertex count BinaryHEAP<hNode> q; int minCostMaxFlow(int s,int t,int f) { int cst = 0; int *const d = dist, *const p = prve; int *const h = pot;//维护 h[u] 表示当前残留网络下s到u的最短距离 //memset(pot,0,sizeof(int)*Ver); while(f>0){ memset(dist,0x3f,sizeof(int)*Ver); q.push(hNode(d[s] = 0, s)); while(q.sz){ hNode x = q[1]; q.pop(); int u = x.second; if(u == t) { q.sz = 0; break; } if(d[u] < x.first) continue; for(eachEage){ int v = to[i]; if(cap[i] && d[v] > d[u] + cost[i] + h[u] - h[v]){ //边距离修正u->v, 根据h的定义,h[v] <= w[e] + h[u] d[v] = d[u] + cost[i] + h[u] - h[v];//最短路径上修正值累加得出的d[v] = d‘[v](实际值) - h[v] (h[s] = 0) p[v] = i; q.push(hNode(d[v], v)); } } } if(d[t] == INF) break; for(int i = 0; i < Ver; i++) h[i] += d[i]; //如果d[i] = INF ??? /* 本题中a == 1 int a = f; // augment flow for(int v = t, e; v != s; v = to[e^1]){ e = p[v]; if(cap[e] < a) a = cap[e]; } */ cst += h[t]; // a*h[t] f --; // -= a for(int v = t, e; v != s; v = to[e^1]){ e = p[v]; cap[e] --; // -= a; cap[e^1] ++;// -= a; } } return cst; } //#define LOCAL int main() { #ifdef LOCAL freopen("in.txt","r",stdin); #endif int n, m; scanf("%d%d",&n,&m); Ver = n; memset(hd,0xff,sizeof(int)*Ver); for(int i = 0; i < m; i++){ int a,b,c; scanf("%d%d%d",&a,&b,&c); Add(--a,--b,1,c); Add(b,a,1,c); } printf("%d\n",minCostMaxFlow(0,n-1,2)); return 0; }
标签:
原文地址:http://www.cnblogs.com/jerryRey/p/4947814.html