zoukankan      html  css  js  c++  java
  • 载谭费用流对偶

    一些你可能需要的前置芝士:

    线性规划问题 线性规划问题是如下的问题:

    (n)非负变量 (x),以及 (m) 个约束,形如:

    [sum_j a_{i,j}x_jleq b_i ]

    要求最大化 (sum_i c_ix_i) 的值,记做:

    [max c^{mathsf T} x\ s.t.\ Axleq b\ xgeq 0\ ]

    一个线性规划的对偶问题是如下的问题:

    (m)非负变量 (y),以及 (n) 个约束,形如:

    [sum_i a_{i,j}y_igeq c_j ]

    要求最小化 (sum_i b_iy_i) 的值,记做:

    [min b^{mathsf T} y\ s.t.\ A^{mathsf T}ygeq c\ ygeq 0\ ]

    关于线性规划问题,我们有如下的定理:

    弱对偶定理 线性规划问题的解总是小于等于其对偶问题的解。

    证明

    [egin{aligned} & sum_i c_ix_i\ leq & sum_i x_isum_j a_{j,i}y_j\ = & sum_j y_jsum_i a_{i,j}x_i\ leq & sum_j y_jb_j end{aligned} ]

    值得注意的是,弱对偶定理对所有的规划问题都成立,包括但不限于整数规划等。

    强对偶定理 线性规划的解等于其对偶问题的解。

    这个定理证明较为复杂,这里略去。

    不同于弱对偶定理,强对偶定理只对线性规划成立。


    我们先来考虑一个线性规划的扩展形式:如何对 (sum_ia_ix_i=b_i) 的条件做对偶?

    解决方案是,我们把它拆成如下两个条件:

    [sum_ia_ix_ileq b_i\ sum_i-a_ix_ileq -b_i ]

    然后记上面那个条件对偶后的变量为 (y_1),下面的为 (y_2),那么最后要最小化的就是 (b_i(y_1-y_2)),于是我们记 (varphi_i=y_1-y_2),此时 (varphi_i) 就没有 (geq 0) 的限制了。

    现在来考虑最大费用循环流的线性对偶形式:

    [max sum w_{u,v}f_{u,v}\ s.t.\ 0leq f_{u,v}leq c_{u,v}\ sum_{v} f_{u,v}-f_{v,u}=0 ]

    于是直接对偶问题可得:

    [min sum c_{u,v}x_{u,v}\ s.t.\ x_{u,v}geq 0\ varphi_u-varphi_v+x_{u,v}geq w_{u,v} ]

    注意到这个形式下,(x_{u,v}) 可以直接取到最小值,于是就等价于

    [min sum c_{u,v}max(0,w_{u,v}-varphi_u+varphi_v) ]

    于是所有这种形式的问题都可以用最大费用循环流建图。


    下面是例题。

    Problem(「ZJOI2013」防守战线).

    长为 (n) 的序列,初始全为 (0),在 (i) 处加 (1)(c_i) 的代价。有 (m) 个限制,形如 ([l_i,r_i]) 的区间和必须 (geq d_i)。求最小代价。

    (nleq 1000,mleq 10000)

    sol.

    记前缀和为 (S_i),于是总的代价为

    [sum_i c_imax(S_i-S_{i-1},0)+sum_iinftymax(S_{l_i-1}-S_{r_i}+d_i,0) ]

    直接拿着跑最大费用循环流即可。可能需要注意一下建图的方式,根据实际测试,不同的建图方式速度差异足足有 (30) 倍。

    #include <bits/stdc++.h>
    
    #define nya(neko...) fprintf(stderr, neko)
    
    constexpr int INF = 0x3f3f3f3f;
    constexpr int maxn = 1005;
    constexpr int maxm = 10005;
    
    int n, m;
    namespace Flow {
    	int s, t, tot = 1, minCost, maxFlow;
    	int first[maxn], cur[maxn];
    	struct Edge { int to, nxt, cap, w; } e[maxn + maxm << 2];
    	
    	inline void Add(int u, int v, int cap, int w) {
    		e[++tot] = { v, first[u], cap, w };
    		first[u] = tot;
    	}
    	inline void Adde(int u, int v, int cap, int w) {
    		Add(u, v, cap, w), Add(v, u, 0, -w);
    	}
    	
    	int phi[maxn]; bool inque[maxn];
    	inline void SPFA() {
    		memset(phi, INF, sizeof phi);
    		std::queue<int> q;
    		
    		phi[s] = 0, q.push(s), inque[s] = 1;
    		while(!q.empty()) {
    			int u = q.front(); inque[u] = 0, q.pop();
    			for(int i = first[u]; i; i = e[i].nxt) {
    				int v = e[i].to;
    				if(e[i].cap && phi[v] > phi[u] + e[i].w) {
    					phi[v] = phi[u] + e[i].w;
    					if(!inque[v]) inque[v] = 1, q.push(v);
    				}
    			}
    		}
    	}
    	
    	inline int cost(int id) { // reduced cost
    		return phi[e[id ^ 1].to] - phi[e[id].to] + e[id].w;
    	}
    	
    	int dis[maxn];
    	inline bool dij() {
    		memset(dis, INF, sizeof dis);
    		
    		using node = std::pair<int, int>;
    		std::priority_queue<node, std::vector<node>, std::greater<node>> q;
    		
    		q.emplace(dis[s] = 0, s);
    		while(!q.empty()) {
    			auto o = q.top(); q.pop();
    			
    			int u = o.second;
    			if(dis[u] != o.first) continue;
    			for(int i = first[u]; i; i = e[i].nxt) {
    				int v = e[i].to;
    				if(e[i].cap && dis[v] > dis[u] + cost(i)) {
    					dis[v] = dis[u] + cost(i);
    					q.emplace(dis[v], v);
    				}
    			}
    		}
    		return dis[t] < INF;
        }
    	
    	bool vis[maxn];
    	inline int DFS(int u, int flow) {
    		if(u == t) return flow;
    		
    		vis[u] = 1;
    
    		int res = 0;
    		for(int &i = cur[u]; i; i = e[i].nxt) {
    			int v = e[i].to;
    			if(vis[v] || !e[i].cap || dis[v] != dis[u] + cost(i)) continue;
    			
    			int f = DFS(v, std::min(flow, e[i].cap));
    			
    			e[i].cap -= f, e[i ^ 1].cap += f;
    			flow -= f, res += f;
    			minCost += f * e[i].w;
    			
    			if(!flow) break;
    		}
    		vis[u] = 0;
    		if(flow) vis[u] = 1;
    		return res;
    	}
    	
    	inline void Dinic() {
    		SPFA();
    		
    		while(dij()) {
    			memcpy(cur, first, sizeof cur);
    			memset(vis, false, sizeof vis);
    			while(int x = DFS(s, INF)) maxFlow += x;
    			
    			for(int i = 0; i <= n; ++i) // all nodes
    				if(dis[i] < INF) phi[i] += dis[i];
    		}
    	}
    }
    using Flow::s;
    using Flow::t;
    using Flow::Adde;
    
    int deg[maxn];
    int main() {
    	scanf("%d%d", &n, &m), s = n + 1, t = n + 2;
    	for(int i = 1, c; i <= n; ++i) {
    		scanf("%d", &c), Adde(i, i - 1, INF, 0);
    		deg[i] += c, deg[i - 1] -= c;
    	}
    	
    	for(int i = 1, l, r, d; i <= m; ++i) {
    		scanf("%d%d%d", &l, &r, &d);
    		Adde(r, l - 1, INF, -d);
    	}
    	
    	for(int i = 0; i <= n; ++i) {
    		if(deg[i] >= 0) Adde(s, i, deg[i], 0);
    		else Adde(i, t, -deg[i], 0);
    	}
    	Flow::Dinic(), printf("%d
    ", -Flow::minCost);
    }
    

    Problem(CF1307G).

    (n) 个点的带权有向图。(q) 组询问,每次给出一个 (X),你可以增加每条边的边权,要求总的变化量不超过 (X),求 (1)(n) 的最短路的最大值。

    (nleq 50,qleq 10^5)

    sol.

    考虑最大费用循环流的一个特化:有源汇最大费用可行流,其形式如下:

    [maxsum w_{u,v}f_{u,v}\ s.t.\ 0leq f_{u,v}leq c_{u,v}\ sum_{u,v}f_{u,v}-f_{v,u}= left{ egin{aligned} 0 & & (u eq s,u eq t)\ F & & (u=s)\ -F & & (u=t)\ end{aligned} ight. ]

    其对偶形式即为:

    [min F(varphi_s-varphi_t)+sum c_{u,v}x_{u,v}\ s.t.\ x_{u,v}geq 0\ x_{u,v}+varphi_u-varphi_vgeq w_{u,v}\ ]

    这道题中,我们考虑最小费用可行流,这就是

    [Flow(s,t,F)=max F(varphi_t-varphi_s)-sum c_{u,v}x_{u,v}\ s.t.\ x_{u,v}geq 0\ varphi_v-varphi_uleq w_{u,v}+x_{u,v}\ ]

    这个形式非常像题目的形式。我们令 (s=1,t=n),并定义 (varphi_1)(0),其他点的 (varphi_u)(1) 号点到它的最短路 (dis_{1,u}),于是 (x_{u,v}) 就是我们题目中要求的变化量。然后令 (c_{u,v}=1) 就有:

    [Flow(1,n,F)=Fcdot dis_{1,n}-X\ dis_{1,n}leq frac{Flow(1,n,F)+X}{F} ]

    于是我们就有:

    [dis_{1,n}=min_F frac{Flow(1,n,F)+X}{F} ]

    然后注意到 (Fleq m),我们预先处理出每个 (F) 的结果,然后不难注意到 (Flow(s,t,F)) 关于 (F) 是凸的,于是可以三分出答案。

    #include <queue>
    #include <cstdio>
    #include <cstring>
    
    constexpr int maxn = 55;
    constexpr int maxm = maxn * maxn;
    constexpr int INF = 0x3f3f3f3f;
    
    int n, m, tot = 1, first[maxn];
    struct Edge { int to, nxt, cap, w; } e[maxm << 1];
    
    inline void Add(int u, int v, int cap, int w) {
    	e[++tot] = { v, first[u], cap, w };
    	first[u] = tot;
    }
    inline void Adde(int u, int v, int cap, int w) {
    	Add(u, v, cap, w), Add(v, u, 0, -w);
    }
    
    int dis[maxn], pre[maxn];
    inline bool SPFA() {
    	static bool inque[maxn];
    	static std::queue<int> q;
    
    	memset(dis, INF, sizeof dis);
    
    	q.push(1), dis[1] = 0, inque[1] = 1;
    	while(!q.empty()) {
    		int u = q.front(); q.pop(), inque[u] = 0;
    		for(int i = first[u]; i; i = e[i].nxt) {
    			int v = e[i].to;
    			if(e[i].cap && dis[v] > dis[u] + e[i].w) {
    				dis[v] = dis[u] + e[i].w, pre[v] = i;
    				if(!inque[v]) q.push(v), inque[v] = 1;
    			}
    		}
    	}
    	return dis[n] < INF;
    }
    
    int q, R, Flow[maxm];
    int main() {
    	scanf("%d%d", &n, &m);
    	for(int i = 1, u, v, w; i <= m; ++i) {
    		scanf("%d%d%d", &u, &v, &w);
    		Adde(u, v, 1, w);
    	}
    
    	R = 0;
    	while(SPFA()) {
    		++R, Flow[R] = Flow[R - 1] + dis[n];
    		for(int u = n; u != 1; u = e[pre[u] ^ 1].to)
    			--e[pre[u]].cap, ++e[pre[u] ^ 1].cap;
    	}
    
    	scanf("%d", &q);
    	for(int X; q --> 0;) {
    		scanf("%d", &X);
    
    		static auto calc = [](int mid, int X) {
    			return 1. * (Flow[mid] + X) / mid;
    		};
    
    		int l = 1, r = R;
    		while(l < r) {
    			int mid = l + r >> 1;
    			if(calc(mid, X) > calc(mid + 1, X)) l = mid + 1;
    			else r = mid;	
    		}
    		printf("%.8lf
    ", calc(l, X));
    	}
    }
    
  • 相关阅读:
    Path Sum II
    Convert Sorted Array to Binary Search Tree
    Construct Binary Tree from Inorder and Postorder Traversal
    Construct Binary Tree from Preorder and Inorder Traversal
    Maximum Depth of Binary Tree
    Binary Tree Zigzag Level Order Traversal
    Binary Tree Level Order Traversal
    Same Tree
    Validate Binary Search Tree
    Binary Tree Inorder Traversal
  • 原文地址:https://www.cnblogs.com/whx1003/p/15512684.html
Copyright © 2011-2022 走看看