一个位于(i,j)的守卫只能控制i行或者j列
我们将这样的一个守卫视为连接i,j的一条无向边,那么我们的目标就是选出花费最小的一组边,使得对每条边定向后,每个点的入度恰好为1
那么对于每个联通块,他就是一个基环外向树,要求花费最小,类比最小生成树的建树过程,建一个最小生成环套树森林qaq
正确性的话….脑补一下?我也没有仔细去想证明qaq
code:
#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn = 210000;
int n,m;
int fa[maxn],v[maxn];
int findfa(const int x){return fa[x]==x?x:fa[x]=findfa(fa[x]);}
struct edge
{
int x,y,c;
friend inline bool operator <(const edge a,const edge b){return a.c<b.c;}
}e[maxn]; int cnt;
ll ans;
int main()
{
//freopen("tmp.in","r",stdin);
//freopen("tmp.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n+m;i++) fa[i]=i,v[i]=0;
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++)
{
int x; scanf("%d",&x);
e[++cnt]=(edge){i,j+n,x};
}sort(e+1,e+cnt+1);
for(int i=1;i<=cnt;i++)
{
int x=findfa(e[i].x),y=findfa(e[i].y);
if(x==y)
{
if(!v[x]) v[x]=1,ans+=e[i].c;
}
else if(!(v[x]&v[y])) ans+=e[i].c,fa[y]=x,v[x]|=v[y];
}
printf("%lld\n",ans);
return 0;
}