@atcoder - Japanese Student Championship 2019 Qualification - E@ Card Collector
@description@
N 个卡片放在 H*W 的方格图上,第 i 张卡片的权值为 Ai,放在 (Ri, Ci)。一个位置可以放置多张卡片。
你可以在每行捡起最多一张卡片,然后在每列捡起最多一张卡片。
求捡起的卡片权值最大和。
Constraints
所有值都是整数。
1≤N≤10^5, 1≤H,W≤10^5, 1≤Ai≤10^5, 1≤Ri≤H, 1≤Ci≤W。
Input
输入的形式如下:
N H W
R1 C1 A1
R2 C2 A2
⋮
⋮
RN CN AN
Output
输出可能的最大权和。
Sample Input
6 2 2
2 2 2
1 1 8
1 1 5
1 2 9
1 2 7
2 1 4
Sample Output
28
样例解释如下:
从第一行捡起第四张卡 A4。
从第二行捡起第六张卡 A6。
从第三行捡起第二张卡 A2。
从第四行捡起第五张卡 A5。
最后权值和 = A4 + A6 + A2 + A5 = 9 + 7 + 4 + 8 = 28。
@solution@
看到这个题就知道它一定是个网络流。
我们源点连向每个卡片,容量为 1,费用为权值;卡片连向它所在的行与列,容量为 1,费用为 0;每行每列向汇点连边,容量为 1,费用为 0。
这样建图跑出来的最大费用流就是答案。
观察这个建图,思考发现它总是先沿着卡片权值最大的路径尝试增广,且它的增广过程是不会撤回的(即以前增广过的卡片不会在某一次增广中被删掉)。
所以:我们可以考虑按权值从大到小加入每张卡片,判断每次加入的卡片是否能与之前的卡片共存。
考虑我们建出来的图实际上一个二分图,我们可以使用 hall 定理判定每次加入卡片后,图中是否依然存在完美匹配。
回想 hall 定理的内容:一个点集 S 的 size <= 它邻集 T 的 size,也可以写作 |T| - |S| >= 0。我们这里的点集的邻集就代表它们所在的行列集合。
我们尝试去找不满足 hall 定理的情况。
假如仅存在单独一个点,则 |T| - |S| = 1。如果加入一个与它不在同一行或同列的点,此时 |T| - |S| 会变大,与我们目的相悖;加入一个与它在同一行或同一列的点时,|T| - |S| 不变,但之后加入的点更有可能使得 |T| - |S| 变小,所以加入这个点更有可能不满足 hall 定理。
于是:我们通过找这个点集同一行同一列的所有点不断扩大点集,到无法扩大时再判断此时的 |T| - |S| 是否满足 hall 定理。
具体到实现,我们可以对行与列建并查集,并查集内统计这个行列集合含多少行多少列(即上文的 |T|)与这些行列上有多少卡片(即上文的 |S|)。
每次加入一张卡片就把它所在的行列集合通过并查集合并,同时维护一下。当然要在加入之前判断是否合法(即是否加入完这张卡片,它所在的集合会出现 |T| - |S| < 0)。
@accepted code@
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 100000;
struct node{
int R, C, A;
friend bool operator < (node a, node b) {
return a.A < b.A;
}
}nd[MAXN + 5];
int fa[2*MAXN + 5], key[2*MAXN + 5], siz[2*MAXN + 5];
int find(int x) {
return fa[x] = (fa[x] == x ? x : find(fa[x]));
}
int N, H, W;
int main() {
long long ans = 0;
scanf("%d%d%d", &N, &H, &W);
for(int i=1;i<=H;i++) fa[i] = i, siz[i] = 1, key[i] = 0;
for(int i=1;i<=W;i++) fa[i + H] = i + H, siz[i + H] = 1, key[i + H] = 0;
for(int i=1;i<=N;i++)
scanf("%d%d%d", &nd[i].R, &nd[i].C, &nd[i].A);
sort(nd + 1, nd + N + 1);
for(int i=N;i>=1;i--) {
int fx = find(nd[i].R), fy = find(nd[i].C + H);
if( fx == fy ) {
if( siz[fx] >= key[fx] + 1 ) {
key[fx]++;
ans += nd[i].A;
}
}
else {
if( siz[fx] + siz[fy] >= key[fx] + key[fy] + 1 ) {
siz[fx] += siz[fy], key[fx] += key[fy] + 1, fa[fy] = fx;
ans += nd[i].A;
}
}
}
printf("%lld\n", ans);
}
@details@
被老师莫名其妙拉去打这种奇怪的比赛。。。
在我印象里,现在 hall 定理的题还是算比较少的吧,记下来记下来。
@atcoder - Japanese Student Championship 2019 Qualification - E@ Card Collector的更多相关文章
- @atcoder - Japanese Student Championship 2019 Qualification - F@ Candy Retribution
目录 @description@ @solution@ @accepted code@ @details@ @description@ 请找到满足以下条件的长度为 N 的非负整数序列 A1, A2, ...
- [AtCoder] NIKKEI Programming Contest 2019 (暂缺F)
[AtCoder] NIKKEI Programming Contest 2019 本来看见这一场的排名的画风比较正常就来补一下题,但是完全没有发现后两题的AC人数远少于我补的上一份AtCoder ...
- [AtCoder] Yahoo Programming Contest 2019
[AtCoder] Yahoo Programming Contest 2019 很遗憾错过了一场 AtCoder .听说这场是涨分场呢,于是特意来补一下题. A - Anti-Adjacency ...
- HDOJ 4336 Card Collector
容斥原理+状压 Card Collector Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/O ...
- HDU 4336:Card Collector(容斥原理)
http://acm.split.hdu.edu.cn/showproblem.php?pid=4336 Card Collector Special Judge Problem Descriptio ...
- Card Collector(HDU 4336)
Card Collector Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)To ...
- hdu4336 Card Collector 状态压缩dp
Card Collector Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Tota ...
- HDU 4336 Card Collector(动态规划-概率DP)
Card Collector Problem Description In your childhood, do you crazy for collecting the beautiful card ...
- HDU 4336 Card Collector 期望dp+状压
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=4336 Card Collector Time Limit: 2000/1000 MS (Java/O ...
随机推荐
- 从默认的index.jsp页面跳转或转发到其他页面
使用forward还是redirect都可以完成跳转 forward:浏览器地址不变,所以存在重复提交的问题 <% pageContext.forward("student/list ...
- oracle习题-emp表查询练习
emp表查询练习 1 查询emp表的全部记录 Select * from emp; 2 查询出每个雇员的编号.姓名.基本工资 Select empno,ename,sal from emp; 3 查询 ...
- 洛谷P1966 [NOIP2013提高组Day1T2]火柴排队
P1966 火柴排队 题目描述 涵涵有两盒火柴,每盒装有 n 根火柴,每根火柴都有一个高度. 现在将每盒中的火柴各自排成一列, 同一列火柴的高度互不相同, 两列火柴之间的距离定义为: ∑(ai-bi) ...
- Vue--vue中的生命周期
Vue的生命周期: 在理解vue生命周期前要把握它的三个重点: 创建-> 改变 -> 销毁 创建: 1.执行beforeCreate 2.监控data 3.注册事件 4.执行create ...
- CSS-DOM的小知识(二)
上篇文章说到,通过element.style.property可以获得元素的样式,但是style属性只能够返回内嵌样式,对于外部样式表的样式和head中的style样式都无法获取,这就限制了此方法的使 ...
- DIV元素垂直居中的分析与实现
首先,对于需要垂直居中的元素常规居中处理 margin: 0 auto; 接下来要设置div的祖先元素的宽高为100%, 在默认的设置中他们都为0: html, body { width: %; he ...
- 2019-8-31-C#-对-byte-数组进行模式搜索
title author date CreateTime categories C# 对 byte 数组进行模式搜索 lindexi 2019-08-31 16:55:58 +0800 2018-07 ...
- 【机器学习PAI实战】—— 玩转人工智能之你最喜欢哪个男生?
摘要: 分类问题是生活中最常遇到的问题之一.普通人在做出选择之前,可能会犹豫不决,但对机器而言,则是唯一必选的问题.我们可以通过算法生成模型去帮助我们快速的做出选择,而且保证误差最小.充足的样本,合适 ...
- golang数据类型二
字符类型 3.14基本数据类型的相互转换 3.15基本数据类型和string的转换 FormatInt // FormatUint 将 int 型整数 i 转换为字符串形式// base:进位制(2 ...
- day39-Spring 08-Spring的AOP:基于AspectJ的注解
基于AspectJ的注解的开发要重点掌握. 这些表达式肯定要应用在我们的某些个增强上. 学习AspectJ也是两种形式:一种是XML,一种是注解.AspectJ的增强,就是那些通知的类型.Aspect ...