题意:
给定一个H行W列的矩阵,在矩阵的格点上放带权值的卡片(一个点上能放多张)。
现在从每行每列各拿走一张卡片(没有可以不拿),求可以拿到的最大权值。
卡片数N<=1e5,H,W<=1e5
思路:
显然可以构造成一个最大费用流模型:每张卡片到它对应的行列各有一条费用0,容量1的边;源点到每张卡片有一条费用为卡片权值,容量1的边;每个行列到汇点有一条费用0,容量1的边。但是边数有5e5,应该会超时吧?
观察这个图发现除去源点和汇点是一张二分图,想到是否可以利用二分图的性质简化问题。
手动模拟一波发现好像只要贪心地从大到小拿,能拿则拿,那么就能得到最佳答案。如何检测是否还能拿呢?
HALL定理:如果一个二分图上,左部|X|<=右部|Y|,如果左部点的任意一个子集U,U相连边对应右部的子集V都有|U|<=|V|,那么这个二分图有最大匹配|X|。
每次拿走点相当于在U中增加点,在V中增加边,用并查集维护一下集合的大小即可。
- 所在行列在同个集合中,直接判断这个集合是否满足条件
- 所在行列不在同一个集合中,那么判断这两个集合合并后是否满足条件,若是则合并
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=2e5+5; struct node{ int r,c,a; friend bool operator<(node a,node b){ return a.a>b.a; } }st[maxn]; int fa[maxn],S[maxn],T[maxn]; int n,H,W; int init(){ for(int i=1;i<=H+W;i++){ fa[i]=i; T[i]=1; } } int find(int x){ return x==fa[x]?x:fa[x]=find(fa[x]); } int main(){ cin>>n>>H>>W; init(); for(int i=1;i<=n;i++){ scanf("%d%d%d",&st[i].r,&st[i].c,&st[i].a); } sort(st+1,st+1+n); ll ans=0; for(int i=1;i<=n;i++){ // printf("%d %d %d\n",st[i].r,st[i].c,st[i].a); int fr=find(st[i].r),fc=find(H+st[i].c); if(fr==fc){//两个在同一集合内 if(T[fc]>=S[fc]+1){ S[fc]++; ans=ans+st[i].a; } } else{//两个不在同一集合内,要合并 if(T[fr]+T[fc]>=S[fr]+S[fc]+1){ fa[fr]=fc; T[fc]+=T[fr]; S[fc]=S[fr]+S[fc]+1; ans=ans+st[i].a; } } } printf("%lld\n",ans); }