棋盘上的守卫 并查集模拟外向基环树

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);

 

以上是关于棋盘上的守卫 并查集模拟外向基环树的主要内容,如果未能解决你的问题,请参考以下文章

bzoj1040[ZJOI2008]骑士 并查集+基环树dp

ZJOI2008 骑士 基环树

数据结构-树并查集的定义及其操作

DFS采集糖果

[WC2005]双面棋盘(并查集+分治)

BZOJ1453[Wc]Dface双面棋盘 线段树+并查集