棋盘上的守卫 并查集模拟外向基环树
Posted dongdong25800
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了棋盘上的守卫 并查集模拟外向基环树相关的知识,希望对你有一定的参考价值。
棋盘上的守卫
基环树就是n个点n条边的树,每个点的入度为1就是外向基环树因为这样的话这个图是往外扩张的,反之内向。
然后这个树自然就只且只有一个环。
题意:n行m列,要求选n个守卫守卫n个行,m个守卫守卫m个列,守卫不能重复,且每个守卫只能守卫行或列,每个守卫一个价值,求最小的代价。
思路:将关于点的图转化为关于边的图,构造一个图含有n+m个点表示n行和m列,题目给的图上的每个点的价值就可以代表点到点的边权了,可以想到如果要覆盖所有的点,就是满足条件的方案,这样的话这个图就是一个每个点都是入度为1的基环树,然后可以给边从小到大排个序,通过并查集模拟生成一个代价最小的外向基环树就行了。对于基环树,可以发现不在乎一个边所指的方向,只要选的边所产生的点集构成的环满足只有一个就好了,所以只需要并查集判断点所在的集合,开个huan[]用来判断一个点所在的集合是否产生了环,合并时对huan取并集就可以了。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=1e5+10;
int fa[2*maxn];
struct note
int x,y,z;
e[2*maxn];
int cmp(note a,note b)
return a.z<b.z;
int huan[2*maxn];
int getf(int x)
return fa[x]=fa[x]==x?x:getf(fa[x]);
int main()
int n,m;
scanf("%d%d",&n,&m);
int cnt=0;
for(int i=1; i<=n; i++)
for(int j=1; j<=m; j++)
int t;
scanf("%d",&t);
note tt;
tt.x=i+m;
tt.y=j;
tt.z=t;
e[++cnt]=tt;
for(int i=1; i<=n+m; i++)
fa[i]=i;
sort(e+1,e+1+cnt,cmp);
ll ans=0;
for(int i=1; i<=cnt; i++)
int x,y,z;
x=e[i].x;
y=e[i].y;
z=e[i].z;
int t1,t2;
t1=getf(x);
t2=getf(y);
if(t1!=t2&&(huan[t1]==0||huan[t2]==0))
fa[t2]=t1; // 这里注意一下:是将t2放到了t1的圈子里,下面才有t1去和t2取并集
huan[t1]|=huan[t2];
ans=ans+z;
if(t1==t2&&huan[t1]==0)
ans=ans+z;
huan[t1]=1;
printf("%lld",ans);
以上是关于棋盘上的守卫 并查集模拟外向基环树的主要内容,如果未能解决你的问题,请参考以下文章