@atcoder - AGC034D@ Manhattan Max Matching
@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\n", 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\n", 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 问题。。。
特别是这个图还是个二分图。。。
想了半天的贪心,然后不停地叉掉,然后继续贪。。。
@atcoder - AGC034D@ Manhattan Max Matching的更多相关文章
- 【杂题】[AGC034D] Manhattan Max Matching【费用流】
Description 有一个无限大的平面,有2N个位置上面有若干个球(可能重复),其中N个位置是红球,N个位置是蓝球,红球与蓝球的总数均为S. 给出2N个位置和上面的球数,现要将红球与蓝球完美匹配, ...
- [AGC034D]Manhattan Max Matching:费用流
前置姿势 \(k\)维空间内两点曼哈顿距离中绝对值的处理 戳这里:[CF1093G]Multidimensional Queries 多路增广的费用流 据说这个东西叫做ZKW费用流? 流程其实很简单, ...
- 「AGC034D」 Manhattan Max Matching
「AGC034D」 Manhattan Max Matching 传送门 不知道这个结论啊... (其实就是菜嘛) 首先 \(O(n^2)\) 的建边显然不太行. 曼哈顿距离有这样一个性质,如果将绝对 ...
- AtCoder ABC 126F XOR Matching
题目链接:https://atcoder.jp/contests/abc126/tasks/abc126_f 题目大意 给定两个整数 M 和 K ,用小于 2M 的的所有自然数,每个两个,用这些数排成 ...
- 【AtCoder】AGC034
AGC034 刷了那么久AtCoder我发现自己还是只会ABCE(手动再见 A - Kenken Race 大意是一个横列,每个点可以跳一步或者跳两步,每个格子是空地或者石头,要求每一步不能走到石头或 ...
- [2019多校联考(Round 6 T3)]脱单计划 (费用流)
[2019多校联考(Round 6 T3)]脱单计划 (费用流) 题面 你是一家相亲机构的策划总监,在一次相亲活动中,有 n 个小区的若干男士和 n个小区的若干女士报名了这次活动,你需要将这些参与者两 ...
- CSS 3学习——transition 过渡
以下内容根据官方规范翻译以及自己的理解整理. 1.介绍 这篇文档介绍能够实现隐式过渡的CSS新特性.文档中介绍的CSS新特性描述了CSS属性的值如何在给定的时间内平滑地从一个值变为另一个值. 2.过渡 ...
- 《算法》第六章部分程序 part 6
▶ 书中第六章部分程序,包括在加上自己补充的代码,包括二分图最大匹配(最小顶点覆盖)的交替路径算法和 HopcroftKarp 算法 ● 二分图最大匹配(最小顶点覆盖)的交替路径算法 package ...
- AtCoder Beginner Contest 247 E - Max Min // 容斥原理
原题链接:E - Max Min (atcoder.jp) 题意: 给定一个数组,求满足最大值为X且最小值为Y的区间个数. 思路:容斥原理 因为必须要包含端点,直接求是不容易的.因此考虑去求不一定包含 ...
随机推荐
- JavaScript模板引擎实例应用(转)
本文将举实例向大家讲解几个常用模板引擎的简单使用. 演示地址:模板引擎示例http://demo.52fhy.com/jstemp/ 准备工作 演示数据:blog.json结构: { "li ...
- Bootstrap启动(关闭)轮播
$('.carousel').carousel(); //启动轮播 $('.carousel').carousel(‘pause’); //关闭轮播 $(‘.carousel’).carousel({ ...
- vue项目及插件
vue项目的创建 方法1: cmd中执行 vue ui vue会创建一个socket,方便快捷 方法2: 命令行建立 vue create v-proj //创建项目名为v-proj的项目文件 > ...
- Docker 私有仓库下载镜像
1.添加私有仓库路径 vim /etc/sysconfig/docker --insecure-registry 192.168.105.30:5000 注:版本差异 2.下载镜像 docker pu ...
- 2019.9.24 csp-s模拟测试51(a) 反思总结
T1:还在头铁,顺便复习了一下lct[虽然这题用不上因为复杂度不对] 头铁结束. 虽然题目存在换根的操作,实际上并不用真的换根. 2操作中求lca的时候只要考虑原树上root和x.y的lca以及x,y ...
- 初探Druid
说到连接池,最常见的就是dbcp和c3p0,关于druid,官方定义是为监控而生的数据库连接池. 官方中文文档地址:https://github.com/alibaba/druid/wiki/%E5% ...
- C# dataGridView_CellValueChanged事件
C# 输入完以后立即更新缓冲区(DataGridView CheckBox列checked变化后就触发CellValueChanged事件) 在DataGridView添加如下的事件( Current ...
- Eclipse Git插件切换分支的时候不要Reset
今天做了一件蠢事,我在当前分支上改了很多代码,后来切换分支的时候,有一个文件有冲突,eclipse提示这个文件冲突,我可以选择commit/stash/reset,我一看这个文件没什么关系,不需要提交 ...
- 转:VMware中CentOS配置静态IP进行网络访问(NAT方式和桥接模式)
传送门:http://blog.csdn.net/zhangatle/article/details/77417310 其实这个博主的博客最是适合新手学习,踩过的坑让我再踩一踩,印象深刻 首先进行NA ...
- 【Pyqt5】自定义信号简单原理(易懂版),多窗口交互,传输数据,调用方法
PS:如果你想在2窗口调用1窗口的内部方法,或者在2窗口传递数据给1窗口数据,本片博客可以放心食用 主窗口: class MainWindow(QWidget,Ui_MainFrom): insert ...