zoukankan      html  css  js  c++  java
  • @codeforces

    @description@

    给定一个有源点与汇点的图 G,并对于每一条边 (u, v) 给定 f(u, v) 与 c(u, v)。f 表示流量,c 表示容量。G 不一定是合法的网络流。

    求一个新图 G',使得 G' 是一个合法网络流(流量守恒与容量限制) ,且 ∑(|f'(u, v) - f(u, v)| + |c'(u, v) - c(u, v)|) 最小。

    输出这个最小值。

    input
    第一行两个整数 n 和 m (2 ≤ n ≤ 100, 0 ≤ m ≤ 100),表示点数与边数。
    接下来 m 行每行 4 个整数 ui, vi, ci, fi (1 ≤ ui, vi ≤ n, ui ≠ vi, 0 ≤ ci, fi ≤ 1 000 000) ,表示图 G 中有一条 (ui, vi) 且容量为 ci,流量为 fi 的图。
    保证 1 为源点而 n 为汇点。输入无自环,但可能有重边。

    output
    输出一个整数,表示最小值。

    sample input1
    2 1
    1 2 2 1
    sample output1
    0
    note
    注意不一定是最大流,比如上面的样例。

    sample input2
    4 2
    2 3 1 1
    3 2 1 1
    sample output2
    0
    note
    可能会有独立于源点和汇点的流存在。

    @solution@

    新的容量 c'(u, v) = max(c(u, v), f'(u, v))。
    贪心一下就可以得到这个结果。

    因此,我们分类讨论:

    (1)c(u, v) >= f(u, v)。
    当我们将 f(u, v) - 1,则费用 + 1。
    因为一条边流量减少等于其反向边流量增加,我们连 (v, u),容量为 f(u, v),费用为 1。
    当我们将 f(u, v) + 1,如果新的流量不超过 c(u, v),则费用 + 1;否则,费用 + 2。
    因为新的流量越大单位费用不会减少,所以我们连两类边:一是 (u, v),容量为 c(u, v) - f(u, v),费用为 1;二是 (u, v),容量为 inf,费用为 2。
    每次沿最短路增广,所以必然先走一类边再走二类边。

    (2)c(u, v) < f(u, v)
    与上面不同的是,这个时候必然会产生费用 f(u, v) - c(u, v)。我们可以先把这部分费用算入答案。
    当 f(u, v) + 1,费用 + 2。连 (u, v),容量为 inf,费用为 2。
    当 f(u, v) - 1,如果新的流量 >= c(u, v),则不会产生费用:因为这部分费用在一开始就被算入了答案;否则费用 + 1。
    因此,我们依然是连两类边:一是 (v, u),容量为 f(u, v) - c(u, v),费用为 0;二是 (v, u),容量为 c(u, v),费用为 1。

    为了使流量守恒,我们统计一个点的进入的流量与出去的流量之差。
    如果进入的流量 > 出去的流量,则多出来的流向一个超级汇点流去,即建边 (i, tt),容量为差值,费用为 0。
    如果进入的流量 < 出去的流量,则差的流由一个超级源点流过来,即建边 (ss, i),容量为差值,费用为 0。

    原图中源点 s 和汇点 t 不一定满足流量守恒怎么办?连边 (t, s),容量为 inf,费用为 0。

    这样子,ss 向 tt 跑最小费用最大流即可。

    @accepted code@

    #include<queue>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int MAXV = 400 + 5;
    const int MAXE = 20000 + 5;
    const int INF = int(1E9);
    struct FlowGraph{
    	struct edge{
    		int to, flow, cap, dis;
    		edge *nxt, *rev;
    	}edges[MAXE], *adj[MAXV], *cur[MAXV], *ecnt;
    	int s, t, cost, dist[MAXV];
    	void init() {
    		ecnt = &edges[0];
    		for(int i=0;i<MAXV;i++) adj[i] = NULL;
    	}
    	void addedge(int u, int v, int c, int w) {
    		edge *p = (++ecnt), *q = (++ecnt);
    		p->to = v, p->flow = 0, p->cap = c, p->dis = w;
    		p->nxt = adj[u], adj[u] = p;
    		q->to = u, q->flow = 0, q->cap = 0, q->dis = -w;
    		q->nxt = adj[v], adj[v] = q;
    		p->rev = q, q->rev = p;
    	}
    	bool inque[MAXV];
    	bool relabel() {
    		queue<int>que;
    		for(int i=0;i<MAXV;i++) dist[i] = INF, cur[i] = adj[i];
    		que.push(s); dist[s] = 0, inque[s] = true;
    		while( !que.empty() ) {
    			int f = que.front(); que.pop(); inque[f] = false;
    			for(edge *p=adj[f];p;p=p->nxt) {
    				if( p->cap > p->flow && dist[f] + p->dis < dist[p->to] ) {
    					dist[p->to] = dist[f] + p->dis;
    					if( !inque[p->to] ) {
    						que.push(p->to);
    						inque[p->to] = true;
    					}
    				}
    			}
    		}
    		return !(dist[t] == INF);
    	}
    	bool vis[MAXV];
    	int aug(int x, int tot) {
    		if( x == t ) {
    			cost += tot*dist[x];
    			return tot;
    		}
    		int sum = 0; vis[x] = true;
    		for(edge *&p=cur[x];p;p=p->nxt) {
    			if( p->cap > p->flow && !vis[p->to] && dist[p->to] == dist[x] + p->dis ) {
    				int del = aug(p->to, min(tot-sum, p->cap-p->flow));
    				p->flow += del, p->rev->flow -= del, sum += del;
    				if( sum == tot ) break;
    			}
    		}
    		vis[x] = false;
    		return sum;
    	}
    	int min_cost_max_flow(int _s, int _t) {
    		s = _s, t = _t; int flow = 0; cost = 0;
    		while( relabel() )
    			flow += aug(s, INF);
    		return flow;
    	}
    }G;
    int deg[MAXV];
    int main() {
    	G.init(); int n, m;
    	scanf("%d%d", &n, &m);
    	int s = 1, t = n, ss = 0, tt = n + 1, ans = 0;
    	for(int i=1;i<=m;i++) {
    		int u, v, c, f; scanf("%d%d%d%d", &u, &v, &c, &f);
    		if( c < f ) {
    			G.addedge(v, u, c, 1);
    			G.addedge(u, v, f - c, 0);
    			G.addedge(u, v, INF, 2);
    			ans += f - c;
    			deg[u] += c, deg[v] -= c;
    		}
    		else {
    			G.addedge(v, u, f, 1);
    			G.addedge(u, v, c - f, 1);
    			G.addedge(u, v, INF, 2);
    			deg[u] += f, deg[v] -= f;
    		}
    	}
    	for(int i=1;i<=n;i++) {
    		if( deg[i] < 0 ) G.addedge(ss, i, -deg[i], 0);
    		if( deg[i] > 0 ) G.addedge(i, tt, deg[i], 0);
    	}
    	G.addedge(t, s, INF, 0);
    	G.min_cost_max_flow(ss, tt);
    	printf("%d
    ", ans + G.cost);
    }
    

    @details@

    除此之外,还有另一种建模方法:

    考虑一个零流的图,它本身会产生一定的费用。
    我们给某一条边的流量增加,费用可能会减少(更接近原图 G 中的流量),不变或者是增加。
    我们建一个图,使得这个图可以满足让流量增加过后费用的变化规律。然后在这个图上找一个最小费用循环流。

  • 相关阅读:
    HDU3336 Count the string —— KMP next数组
    CodeForces
    51Nod 1627 瞬间移动 —— 组合数学
    51Nod 1158 全是1的最大子矩阵 —— 预处理 + 暴力枚举 or 单调栈
    51Nod 1225 余数之和 —— 分区枚举
    51Nod 1084 矩阵取数问题 V2 —— 最小费用最大流 or 多线程DP
    51Nod 机器人走方格 V3 —— 卡特兰数、Lucas定理
    51Nod XOR key —— 区间最大异或值 可持久化字典树
    HDU4825 Xor Sum —— Trie树
    51Nod 1515 明辨是非 —— 并查集 + 启发式合并
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/10384431.html
Copyright © 2011-2022 走看看