给一个
N
×
M
≤
1
e
5
N×M\leq1e5
N×M≤1e5的棋盘,然后每个棋盘都可以放一个守卫,你可以选择是让它保护横向的也可以让他保护纵向的。然后一个位置最多只能放一个守卫,放置在
(
i
,
j
)
(i,j)
(i,j)这个位置的代价是
w
i
,
j
w_{i,j}
wi,j。然后求使得所有的位置都被守护的最小代价。
完全不太会做的题。
一种费用流/最大权匹配的做法是左边放
N
+
M
N+M
N+M个点分别表示行/列,右边是所有的坐标,中间连代价大小的边,求最大权匹配。
但复杂度会比较大,这里的做法是把
i
i
i和
j
j
j连
w
i
,
j
w_{i,j}
wi,j大小的边。然后求一个基环树,因为相当于每个点都要被选到,并且入度为
1
1
1。
求基环树用克鲁斯卡尔维护。
如果当前两个点在同一集合,那么判断是否已经成环,如果不成环还可以加上这一条边。
如果当前两个点不在同一个集合,那么判断是否存在一个点所在集合没有成环,如果是可以加边。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int N=2e5+7;
struct edge {
int u,v,w;
bool operator <(const edge &rhs) const {
return w<rhs.w;
}
}e[N];
bool cir[N];
int fa[N];
int tot=0;
int find(int x) {
if(x==fa[x]) return fa[x];
else return fa[x]=find(fa[x]);
}
int main() {
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++) {
int w;
scanf("%d",&w);
e[++tot].u=i;
e[tot].v=n+j;
e[tot].w=w;
}
}
sort(e+1,e+1+tot);
ll ans=0;
for(int i=1;i<=n+m;i++) fa[i]=i;
for(int i=1;i<=tot;i++) {
int U=find(e[i].u);
int V=find(e[i].v);
if(U==V) {
if(!cir[U]) {
cir[U]=1;
ans+=e[i].w;
}
}
else if(!cir[U]||!cir[V]) {
fa[U]=V;
ans+=e[i].w;
cir[V]|=cir[U];
}
}
printf("%lld\n",ans);
return 0;
}