@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@

  1. #include<cstdio>
  2. #include<algorithm>
  3. using namespace std;
  4. typedef long long ll;
  5. const int MAXV = 4000;
  6. const int MAXE = 20000;
  7. const int INF = (1<<30);
  8. const ll LINF = (1LL<<60);
  9. struct FlowGraph{
  10. struct edge{
  11. int to, cap, flow; ll dis;
  12. edge *nxt, *rev;
  13. }edges[MAXE + 5], *adj[MAXV + 5], *cur[MAXV + 5], *ecnt;
  14. FlowGraph() {ecnt = &edges[0];}
  15. int s, t;
  16. void addedge(int u, int v, int c, ll w) {
  17. edge *p = (++ecnt), *q = (++ecnt);
  18. p->to = v, p->cap = c, p->flow = 0, p->dis = -w;
  19. p->nxt = adj[u], adj[u] = p;
  20. q->to = u, q->cap = 0, q->flow = 0, q->dis = w;
  21. q->nxt = adj[v], adj[v] = q;
  22. p->rev = q, q->rev = p;
  23. // printf("%d %d %d %lld\n", u, v, c, w);
  24. }
  25. ll h[MAXV + 5], d[MAXV + 5], f[MAXV + 5];
  26. int hp[MAXV + 5];
  27. void update(int x, ll k) {
  28. f[x] = k;
  29. while( x ) {
  30. hp[x] = x;
  31. if( (x<<1) <= t && f[hp[x]] > f[hp[x<<1]] )
  32. hp[x] = hp[x<<1];
  33. if( (x<<1|1) <= t && f[hp[x]] > f[hp[x<<1|1]] )
  34. hp[x] = hp[x<<1|1];
  35. x >>= 1;
  36. }
  37. }
  38. bool relabel() {
  39. for(int i=s;i<=t;i++)
  40. h[i] += d[i], d[i] = f[i] = LINF, hp[i] = i, cur[i] = adj[i];
  41. update(s, d[s] = 0);
  42. while( f[hp[1]] != LINF ) {
  43. int x = hp[1]; update(x, LINF);
  44. for(edge *p=adj[x];p;p=p->nxt) {
  45. if( p->cap > p->flow ) {
  46. ll w = p->dis + h[x] - h[p->to];
  47. if( d[x] + w < d[p->to] )
  48. update(p->to, d[p->to] = d[x] + w);
  49. }
  50. }
  51. }
  52. return !(d[t] == LINF);
  53. }
  54. bool vis[MAXV + 5];
  55. int aug(int x, int tot) {
  56. if( x == t ) return tot;
  57. int sum = 0; vis[x] = true;
  58. for(edge *&p=cur[x];p;p=p->nxt) {
  59. ll w = p->dis + h[x] - h[p->to];
  60. if( !vis[p->to] && p->cap > p->flow && d[x] + w == d[p->to] ) {
  61. int del = aug(p->to, min(tot - sum, p->cap - p->flow));
  62. p->flow += del, p->rev->flow -= del, sum += del;
  63. if( sum == tot ) break;
  64. }
  65. }
  66. vis[x] = false;
  67. return sum;
  68. }
  69. ll min_cost_max_flow(int _s, int _t) {
  70. s = _s, t = _t; ll cost = 0;
  71. while( relabel() ) {
  72. int del = aug(s, INF);
  73. cost += (d[t] + h[t]) * del;
  74. }
  75. return -cost;
  76. }
  77. }G;
  78. int N, s, t;
  79. int main() {
  80. scanf("%d", &N), s = 1, t = 2*N+6;
  81. for(int i=1;i<=N;i++) {
  82. int x, y, c; scanf("%d%d%d", &x, &y, &c);
  83. G.addedge(s, i+1, c, 0);
  84. G.addedge(i+1, 2*N+2, INF, + x + y);
  85. G.addedge(i+1, 2*N+3, INF, + x - y);
  86. G.addedge(i+1, 2*N+4, INF, - x + y);
  87. G.addedge(i+1, 2*N+5, INF, - x - y);
  88. }
  89. for(int i=1;i<=N;i++) {
  90. int x, y, c; scanf("%d%d%d", &x, &y, &c);
  91. G.addedge(i+N+1, t, c, 0);
  92. G.addedge(2*N+5, i+N+1, INF, + x + y);
  93. G.addedge(2*N+4, i+N+1, INF, + x - y);
  94. G.addedge(2*N+3, i+N+1, INF, - x + y);
  95. G.addedge(2*N+2, i+N+1, INF, - x - y);
  96. }
  97. printf("%lld\n", G.min_cost_max_flow(s, t));
  98. }
  99. /*
  100. (+ rx + ry) + (- bx - by)
  101. (+ rx - ry) + (- bx + by)
  102. (- rx + ry) + (+ bx - by)
  103. (- rx - ry) + (+ bx + by)
  104. */

@details@

为什么我会傻到以为图匹配是 NP 问题。。。

特别是这个图还是个二分图。。。

想了半天的贪心,然后不停地叉掉,然后继续贪。。。

@atcoder - AGC034D@ Manhattan Max Matching的更多相关文章

  1. 【杂题】[AGC034D] Manhattan Max Matching【费用流】

    Description 有一个无限大的平面,有2N个位置上面有若干个球(可能重复),其中N个位置是红球,N个位置是蓝球,红球与蓝球的总数均为S. 给出2N个位置和上面的球数,现要将红球与蓝球完美匹配, ...

  2. [AGC034D]Manhattan Max Matching:费用流

    前置姿势 \(k\)维空间内两点曼哈顿距离中绝对值的处理 戳这里:[CF1093G]Multidimensional Queries 多路增广的费用流 据说这个东西叫做ZKW费用流? 流程其实很简单, ...

  3. 「AGC034D」 Manhattan Max Matching

    「AGC034D」 Manhattan Max Matching 传送门 不知道这个结论啊... (其实就是菜嘛) 首先 \(O(n^2)\) 的建边显然不太行. 曼哈顿距离有这样一个性质,如果将绝对 ...

  4. AtCoder ABC 126F XOR Matching

    题目链接:https://atcoder.jp/contests/abc126/tasks/abc126_f 题目大意 给定两个整数 M 和 K ,用小于 2M 的的所有自然数,每个两个,用这些数排成 ...

  5. 【AtCoder】AGC034

    AGC034 刷了那么久AtCoder我发现自己还是只会ABCE(手动再见 A - Kenken Race 大意是一个横列,每个点可以跳一步或者跳两步,每个格子是空地或者石头,要求每一步不能走到石头或 ...

  6. [2019多校联考(Round 6 T3)]脱单计划 (费用流)

    [2019多校联考(Round 6 T3)]脱单计划 (费用流) 题面 你是一家相亲机构的策划总监,在一次相亲活动中,有 n 个小区的若干男士和 n个小区的若干女士报名了这次活动,你需要将这些参与者两 ...

  7. CSS 3学习——transition 过渡

    以下内容根据官方规范翻译以及自己的理解整理. 1.介绍 这篇文档介绍能够实现隐式过渡的CSS新特性.文档中介绍的CSS新特性描述了CSS属性的值如何在给定的时间内平滑地从一个值变为另一个值. 2.过渡 ...

  8. 《算法》第六章部分程序 part 6

    ▶ 书中第六章部分程序,包括在加上自己补充的代码,包括二分图最大匹配(最小顶点覆盖)的交替路径算法和 HopcroftKarp 算法 ● 二分图最大匹配(最小顶点覆盖)的交替路径算法 package ...

  9. AtCoder Beginner Contest 247 E - Max Min // 容斥原理

    原题链接:E - Max Min (atcoder.jp) 题意: 给定一个数组,求满足最大值为X且最小值为Y的区间个数. 思路:容斥原理 因为必须要包含端点,直接求是不容易的.因此考虑去求不一定包含 ...

随机推荐

  1. SSM3-SVN的安装和搭建环境

    1.安装svn 2.创建仓库 3.设置用户 . 4.eclipse和svn的集成 eclipse里安装SVN插件,一般来说,有两种方式: 直接下载SVN插件,将其解压到eclipse的对应目录里 使用 ...

  2. mac上mamp用navigate连接不上

    参考 http://blog.sina.com.cn/s/blog_6742643c0100r9qp.html

  3. Linux & CentOS & RHEL

    1.修改centos7的系统编码:https://blog.csdn.net/violet_echo_0908/article/details/58063555 2.windows 环境下使用ultr ...

  4. Java是如何实现跨平台的

    一.Java是如何实现跨平台的 1.我们编写的Java源码,编译后会生成一种 .class 文件,称为字节码文件 2.Java虚拟机JVM就是负责将字节码文件翻译成特定平台下的机器码然后运行.也就是说 ...

  5. NOIP模拟 6.26

    T1 子矩阵 题目描述 小A有一个N×M的矩阵,矩阵中1~N*M这(N*M)个整数均出现过一次.现在小A在这个矩阵内选择一个子矩阵,其权值等于这个子矩阵中的所有数的最小值.小A想知道,如果他选择的子矩 ...

  6. golang在import自己的包报错问题

    原因:使用git clone项目后,项目根路径是小写英文名称,比如cmdbapi,但是项目里面的import导入自己的相关包时,红色报错 解决:把项目名称改写成import导入包的名称,即cmdbAp ...

  7. 2018-8-10-win10-UWP-修改密码框文字水平

    title author date CreateTime categories win10 UWP 修改密码框文字水平 lindexi 2018-08-10 19:17:19 +0800 2018-2 ...

  8. Linux常用命令4 帮助命令

    1.帮助命令:man 命令英文原意:manual 命令所在路径:/usr/bin/man    执行权限:所有用户 语法:man [命令或配置文件] 功能描述:获得命令或者配置文件的帮助信息 例如:m ...

  9. 关于android SDK安装Failed to fetch URL http://dl-ssl.google.com/android/repository/addons_list-1.xml出错

          最近SDK出问题了,然后在google下载了一个android-sdk-windows.rar,然后点击SDK Manager,结果一直不能刷新API Level,然后就开始在网上找了好多 ...

  10. jdbc连接数据库的步骤(转载)

    http://hzy3774.iteye.com/blog/1689525   1.加载JDBC驱动程序: 在连接数据库之前,首先要加载想要连接的数据库的驱动到JVM(Java虚拟机), 这通过jav ...