• @atcoder



    @description@

    考虑一个二维平面,执行共 2*N 次操作:
    前 N 次,第 i 次在 (rx, ry) 处放置 rc 个红色球;
    后 N 次,第 i 次在 (bx, by) 处放置 bc 个蓝色球。

    保证放置的红色球总数 = 放置的蓝色球总数。
    请将这些球两两配对,使得所有配对中 (bx, by) 与 (rx, ry) 的 |rx - bx| + |ry - by| 之和最大。

    Constraints
    1≤N≤1000, 0≤RXi,RYi,BXi,BYi≤10^9, 1≤RCi,BCi≤10
    ∑RCi=∑BCi

    Input
    输入形式如下:
    N
    RX1 RY1 RC1
    RX2 RY2 RC2

    RXN RYN RCN
    BX1 BY1 BC1
    BX2 BY2 BC2

    BXN BYN BCN

    Output
    输出配对后曼哈顿距离之和的最大值。

    Sample Input 1
    2
    0 0 1
    3 2 1
    2 2 1
    5 0 1
    Sample Output 1
    8

    Sample Input 2
    Copy
    3
    0 0 1
    2 2 1
    0 0 2
    1 1 1
    1 1 1
    3 3 2
    Sample Output 2
    16

    @solution@

    假如不考虑数据范围,我们可以将所有红球与所有蓝球连边,边权为匹配的曼哈顿距离。
    然后跑一个二分图的最大权完美匹配。

    这看起来非常好,但是 N <= 1000,边建出来一共有 N^2 条,会炸。
    我们考虑怎么才能优化建图。

    注意我们为什么要建 N^2 条边:哈密顿距离中带有绝对值,使得边权由两点共同决定。
    在最大化问题中,有一个去掉绝对值的常用方法(当然不是零点分段):|x| = max{x, -x}。
    这道题同理,|rx - bx| + |ry - by| 可以表示成以下几种情况的最大值:
    (rx - bx) + (ry - by) = (+ rx + ry) + (- bx - by)
    (rx - bx) + (by - ry) = (+ rx - ry) + (- bx + by)
    (bx - rx) + (ry - by) = (- rx + ry) + (+ bx - by)
    (bx - rx) + (by - ry) = (- rx - ry) + (+ bx + by)

    这样的转化使得边权可以由两点分别的权之和决定,就可以拆开,不用再建 O(n^2) 条边了。
    具体来说,我们先 s 向 N 个红球连容量为 rc,费用为 0 的边;再 N 个蓝球向 t 连容量为 bc,费用为 0 的边。
    中间另建 4 个点,表示以上 4 种情况。N 个红球连过去 4 个点,4 个点连向 N 个蓝球,费用如上面所谈论的。
    注意 N 个红球与 N 个蓝球要交错着连(正正 对应 负负)。

    @accepted code@

    #include<cstdio>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const int MAXV = 4000;
    const int MAXE = 20000;
    const int INF = (1<<30);
    const ll LINF = (1LL<<60);
    struct FlowGraph{
    	struct edge{
    		int to, cap, flow; ll dis;
    		edge *nxt, *rev;
    	}edges[MAXE + 5], *adj[MAXV + 5], *cur[MAXV + 5], *ecnt;
    	FlowGraph() {ecnt = &edges[0];}
    	int s, t;
    	void addedge(int u, int v, int c, ll w) {
    		edge *p = (++ecnt), *q = (++ecnt);
    		p->to = v, p->cap = c, p->flow = 0, p->dis = -w;
    		p->nxt = adj[u], adj[u] = p;
    		q->to = u, q->cap = 0, q->flow = 0, q->dis = w;
    		q->nxt = adj[v], adj[v] = q;
    		p->rev = q, q->rev = p;
    //		printf("%d %d %d %lld
    ", u, v, c, w);
    	}
    	ll h[MAXV + 5], d[MAXV + 5], f[MAXV + 5];
    	int hp[MAXV + 5];
    	void update(int x, ll k) {
    		f[x] = k;
    		while( x ) {
    			hp[x] = x;
    			if( (x<<1) <= t && f[hp[x]] > f[hp[x<<1]] )
    				hp[x] = hp[x<<1];
    			if( (x<<1|1) <= t && f[hp[x]] > f[hp[x<<1|1]] )
    				hp[x] = hp[x<<1|1];
    			x >>= 1;
    		}
    	}
    	bool relabel() {
    		for(int i=s;i<=t;i++)
    			h[i] += d[i], d[i] = f[i] = LINF, hp[i] = i, cur[i] = adj[i];
    		update(s, d[s] = 0);
    		while( f[hp[1]] != LINF ) {
    			int x = hp[1]; update(x, LINF);
    			for(edge *p=adj[x];p;p=p->nxt) {
    				if( p->cap > p->flow ) {
    					ll w = p->dis + h[x] - h[p->to];
    					if( d[x] + w < d[p->to] )
    						update(p->to, d[p->to] = d[x] + w);
    				}
    			}
    		}
    		return !(d[t] == LINF);
    	}
    	bool vis[MAXV + 5];
    	int aug(int x, int tot) {
    		if( x == t ) return tot;
    		int sum = 0; vis[x] = true;
    		for(edge *&p=cur[x];p;p=p->nxt) {
    			ll w = p->dis + h[x] - h[p->to];
    			if( !vis[p->to] && p->cap > p->flow && d[x] + w == d[p->to] ) {
    				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;
    	}
    	ll min_cost_max_flow(int _s, int _t) {
    		s = _s, t = _t; ll cost = 0;
    		while( relabel() ) {
    			int del = aug(s, INF);
    			cost += (d[t] + h[t]) * del;
    		}
    		return -cost;
    	}
    }G;
    int N, s, t;
    int main() {
    	scanf("%d", &N), s = 1, t = 2*N+6;
    	for(int i=1;i<=N;i++) {
    		int x, y, c; scanf("%d%d%d", &x, &y, &c);
    		G.addedge(s, i+1, c, 0);
    		G.addedge(i+1, 2*N+2, INF, + x + y);
    		G.addedge(i+1, 2*N+3, INF, + x - y);
    		G.addedge(i+1, 2*N+4, INF, - x + y);
    		G.addedge(i+1, 2*N+5, INF, - x - y);
    	}
    	for(int i=1;i<=N;i++) {
    		int x, y, c; scanf("%d%d%d", &x, &y, &c);
    		G.addedge(i+N+1, t, c, 0);
    		G.addedge(2*N+5, i+N+1, INF, + x + y);
    		G.addedge(2*N+4, i+N+1, INF, + x - y);
    		G.addedge(2*N+3, i+N+1, INF, - x + y);
    		G.addedge(2*N+2, i+N+1, INF, - x - y);
    	}
    	printf("%lld
    ", G.min_cost_max_flow(s, t));
    }
    /*
    (+ rx + ry) + (- bx - by)
    (+ rx - ry) + (- bx + by)
    (- rx + ry) + (+ bx - by)
    (- rx - ry) + (+ bx + by)
    */
    

    @details@

    为什么我会傻到以为图匹配是 NP 问题。。。
    特别是这个图还是个二分图。。。
    想了半天的贪心,然后不停地叉掉,然后继续贪。。。

  • 相关阅读:
    HTML5 在canvas中绘制复杂形状
    干货!最佳谈薪模板分享
    深度解析大学生“白菜价”现状
    【maven】maven 项目 deploy 报错:Failed to execute goal org.apache.maven.plugins:maven-deploy-plugin:2.7
    【maven】Maven插件篇 --- maven项目 mvn install 报错: Failed to execute goal on project 项目名: Could not resolve dependencies for project
    【Maven】maven项目 mvn clean install 或 build,报错:Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.12.4:test
    【IDEA】idea 新创建文件 增加 注释模板
    【IDEA】idea一直不停的scanning files to index 解决方法
    【mysql】extra 中的 FirstMatch()解读
    【linux】linux 中 > 和 >>的区别, > 是代替rm 的安全方式
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11735332.html
Copyright © 2020-2023  润新知