并查集PKU2492A Bug's Life解题报告

这题和1703几乎是一样的,就是看两个点之间的点是偶数还是基数,关键是记录下每一个点的权数(即每一点在树中的层数)。。。

比如像两个串1-2-3和 4-5-6-7,如果吧2和6连,那么怎么去改变合成串中个点的权数是个头疼的问题,只要这个问题解决,那么问题也就迎刃而解了。

典型思路,用fa[i]代表i的父节点,这里进行路径压缩,所以每个i的父节点都是它所在的树的根结点。。用pv[i]代表i的权数,也就是节点i所处在树中的层数(其实由于路径压缩,所有的树就只有两层,一层是根结点,一层是子节点,但是为了计算,把每一节点应该所在的层记下了)。。。

那么,核心代码为:

void init(int n){
int i;
for(i=0;i<=n;i++){
   fa[i]=-1;                                          //初始化每一节点的父节点都是-1,每一点的层数都是0;
   pv[i]=0;
}
}
int find(int i){
int r;
if(fa[i]<0)
   return i;                                                    //若i为根结点,则返回i的值
r=find(fa[i]);                                               //寻找i的根结点
pv[i]+=pv[fa[i]];                                           //注意这不很重要,如果是一个点一个点的加,则这一步没什么作用,因为fa[i]肯定是根结点,其权数为0,但若出现两个树的子节点相连,如1-2-3,4-5-6-7.然后3和6 相连,就很重要,因为连完后,4的权数变为2,而567的全书也要变化,这一变化不是直接就变的,而是通过以后每一次find操作,给567加上2,形成相连后的层数!!!
fa[i]=r;
return r;
}

void unit(int a,int b){
int ra=find(a);                                       //通过find 操作,把a,b的权数调整为争取的权数。
int rb=find(b);
if(pv[a]>pv[b]){                                     //若是1-2,连接2-3,就必须要判断2和3的权数大小,要不然输入2,3和输入3,2会得到不同的答案。。。
   fa[rb]=ra;
   pv[rb]=pv[a]-pv[b]+1;
}
else{
   fa[ra]=rb;
   pv[ra]=pv[b]-pv[a]+1;
}                        
}

 

AC代码:

#include<stdio.h>
#define m1 2005
int fa[m1],pv[m1];
void init(int n){
int i;
for(i=0;i<=n;i++){
   fa[i]=-1;
   pv[i]=0;
}
}
int find(int i){
int r;
if(fa[i]<0)
   return i;
r=find(fa[i]);
pv[i]+=pv[fa[i]];
fa[i]=r;
return r;
}

void unit(int a,int b){
int ra=find(a);
int rb=find(b);
if(pv[a]>pv[b]){
   fa[rb]=ra;
   pv[rb]=pv[a]-pv[b]+1;
}
else{
   fa[ra]=rb;
   pv[ra]=pv[b]-pv[a]+1;
}

}
void main(){
int i,n1,a,b,count=0,flag,m,n;
scanf("%d",&n1);
while(n1--){
   flag=1;
   count++;
   scanf("%d%d",&n,&m);
   init(n);
   for(i=0;i<m;i++){
     scanf("%d%d",&a,&b);
    if(flag){
     if(find(a)!=find(b))
      unit(a,b);
     else{
      if((pv[a]+pv[b])%2==0){
       printf("Scenario #%d:/nSuspicious bugs found!/n/n", count);
                        flag=0;
      }
     }
    }
   }
   if(flag)
    printf ( "Scenario #%d:/nNo suspicious bugs found!/n/n", count );
}
}

 

同一类型的题目还有1988cube stacking:

http://acm.pku.edu.cn/JudgeOnline/problem?id=1988

ac代码:

#include<stdio.h>
#define MAX 30001
int fa[MAX],down[MAX],up[MAX];
void init(){
int i;
for(i=0;i<MAX;i++){
   fa[i]=i;
   down[i]=1;
   up[i]=0;
}
}
void link(int x,int y){
fa[y]=x;
up[y]=down[x];
down[x]+=down[y];
}
int getup(int top,int a){
if(fa[a]!=top){
   up[a]+=getup(top,fa[a]);
   fa[a]=top;
}
return up[a];
}
int find(int i){
int r;
if(fa[i]!=i){
   r=find(fa[i]);
   getup(r,i);                                  //同样这里每一次find操作都会把需要改变的up[i]的值改变掉。。
}
return fa[i];
}
void unit(int x,int y){
link(find(x),find(y));
}
void main(){
int i,n,a,b;
char c;
scanf("%d",&n);
init();
while(n--){
   getchar();
   scanf("%c",&c);
   if(c=='M'){
    scanf("%d%d",&a,&b);
    unit(a,b);
   }
   else if(c=='C'){
    scanf("%d",&a);
    printf("%d/n",down[find(a)]-up[a]-1);
   }
}
}

并查集其它题:http://acm.pku.edu.cn/JudgeOnline/problem?id=1611 the suspect

http://acm.pku.edu.cn/JudgeOnline/problem?id=2985 The k-th Largest Group    (未完成)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值