AtCoder 5168 - Card Collector (二分图匹配的HALL定理)

Problem Statement
There are N cards placed on a grid with H rows and W columns of squares.

The i-th card has an integer Ai written on it, and it is placed on the square at the Ri-th row from the top and the Ci-th column from the left.

Multiple cards may be placed on the same square.

You will first pick up at most one card from each row.

Then, you will pick up at most one card from each column.

Find the maximum possible sum of the integers written on the picked cards.

Constraints
All values are integers.
1≤N≤105
1≤H,W≤105
1≤Ai≤105
1≤Ri≤H
1≤Ci≤W
Input
Input is given from Standard Input in the following format:

N H W
R1 C1 A1
R2 C2 A2
\vdots
R_N C_N A_N
Output
Print the maximum possible sum of the integers written on the picked cards.

Sample Input 1
6 2 2
2 2 2
1 1 8
1 1 5
1 2 9
1 2 7
2 1 4
Sample Output 1
28
The sum of the integers written on the picked cards will be 28, the maximum value possible, if you pick up cards as follows:

Pick up the fourth card from the first row.
Pick up the sixth card from the second row.
Pick up the second card from the first column.
Pick up the fifth card from the second column.
Sample Input 2
13 5 6
1 3 35902
4 6 19698
4 6 73389
3 6 3031
3 1 4771
1 4 4784
2 1 36357
2 1 24830
5 6 50219
4 6 22645
1 2 30739
1 4 68417
1 5 78537
Sample Output 2
430590
Sample Input 3
1 100000 100000
1 1 1
Sample Output 3
1

题意:
给定一个H行W列的矩阵,在矩阵的格点上放带权值的卡片(一个点上能放多张)。现在从每行每列各拿走一张卡片(没有可以不拿),求可以拿到的最大权值。
卡片数N<=1e5,H,W<=1e5

很容易就想到是二分图,然后当场打了一发二分图最大匹配TLE了。
后来了解到神奇的HALL定理:
如果一个二分图上,左部 ∣ X ∣ < = |X|<= X<=右部 ∣ Y ∣ |Y| Y,如果左部点的任意一个子集 U U U, U U U相连边对应右部的子集 V V V都有 ∣ U ∣ < = ∣ V ∣ |U|<=|V| U<=V,那么这个二分图有最大匹配 ∣ X ∣ |X| X

先对所有的卡片从大到小排序,优先取权值大。每个卡片对应有一个 x x x坐标和 y y y坐标。为了使得已经选取了的卡片在最大匹配内,我们要维护选取的自己 ∣ U ∣ |U| U恒小于等于 ∣ V ∣ |V| V
每次取走卡片相当于在 U U U中增加点,在 V V V中增加对应的点,用并查集维护一下集合的大小即可。
如果卡片的所在行列在同个集合中,直接判断这个集合是否满足条件
所在行列不在同一个集合中,那么判断这两个集合合并后是否满足条件,若是则合并

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int MAXN = 1e5 + 10;

struct node{
    int u,v;
    ll w;
}edge[MAXN];

bool cmp(node a,node b){return a.w > b.w;}

int f[MAXN<<1],sz[MAXN<<1];

int find(int x)
{
    int t = x;
    while (x != f[x]) x = f[x];
    while (t != f[t])
    {
        int tmp = f[t];
        f[t] = x;
        t = tmp;
    }
    return x;
}

int main()
{
    int n,h,w;
    scanf("%d%d%d",&n,&h,&w);
    for (int i = 1;i<=n;i++)
    {
        scanf("%d%d%lld",&edge[i].u,&edge[i].v,&edge[i].w);
    }
    sort(edge+1,edge + n + 1,cmp);
    ll ans = 0;
    for (int i = 1;i<=h+w;i++) f[i] = i,sz[i] = 1;
    for (int i = 1;i<=n;i++)
    {
        int fu = find(edge[i].u),fv = find(edge[i].v + h);;
        if (fu == fv)
        {
            if (sz[fu] > 0)
            {
                ans += edge[i].w;
                sz[fu]--;
            }
        }
        else
        {
            if (sz[fu] + sz[fv] > 0)
            {
                f[fv] = fu;
                sz[fu] += sz[fv] - 1;
                ans += edge[i].w;
            }
        }
    }
    printf("%lld",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值