bzoj 4883 [Lydsy1705月赛]棋盘上的守卫——并查集(思路!)
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4883
把各行和各列看成n+m个点。
如果一下能防守行和列,就是最大匹配了。这是每两个左右部点需要一条边。
现在一行和一列都需要专门防守,其实可以看成每个点都需要一条边!
记录并查集内部已经有没有环,在连边的讨论一下即可。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; const int N=1e5+5; int n,m,xnt,fa[N]; ll ans; bool fx[N]; struct Ed{ int x,y,w; Ed(int x=0,int y=0,int w=0):x(x),y(y),w(w) {} bool operator< (const Ed &b) const {return w<b.w;} }ed[N]; int rdn() { int ret=0;bool fx=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();} while(ch>='0'&&ch<='9') ret=(ret<<3)+(ret<<1)+ch-'0',ch=getchar(); return fx?ret:-ret; } void add(int x,int y,int z) { ed[++xnt]=Ed(x,y,z); } int find(int a){return fa[a]==a?a:fa[a]=find(fa[a]);} int main() { n=rdn(); m=rdn(); for(int i=1;i<=n;i++) for(int j=1,w;j<=m;j++) { w=rdn(); add(i,j+n,w); } int d=n+m; for(int i=1;i<=d;i++) fa[i]=i; sort(ed+1,ed+xnt+1); for(int i=1,u,v;i<=xnt;i++) { u=find(ed[i].x); v=find(ed[i].y); if(u!=v&&(fx[u]&fx[v])==0) { fa[u]=v; fx[v]|=fx[u]; ans+=ed[i].w; } else if(u==v&&!fx[u]) { fx[u]=1; ans+=ed[i].w; } } printf("%lld\n",ans); return 0; }