POJ 2014.10.19 并查集刷题记录

2014.10.19各种强化练习并查集(虽然正题是排列组合。。。) 
POJ的界面无语了,刷了一天的题,只有食物链是中文版。。。题意各种不解,各种坑爹翻译,各种脑补题意。
题目不按顺序排列,大致按难度升序排序;
先贴出并查集简单操作
初始化
</pre><pre name="code" class="cpp">for (i=1;i<=n;++i)
  father[i]=i;
查找
int find(int x)//(路径压缩)
{
  if (x!=father[x])
   father[x]=find(father[x]);
 return father[x];
}
合并
int union(int x,int y)
{
  int r1,r2;
  r1=find(x); r2=find(y);
  if (r1!=r2)
    father[r1]=r2;//(视具体情况合并关系不同)
Easy
    1.POJ 1308   Is It A Tree?
        大致题意:输入一组点之间的关系(儿子指向父亲),最后判断是否为一棵树(多组数据)
        简单并查集操作,合并时判断两结点是否已在同一集合; 若有在同一集合的点或不为一棵树(遗漏点)。
code:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int father[100001],root,use[100001];
int find(int x)
{
if (x!=father[x])
 father[x]=find(father[x]);
return father[x];
}
int main()
{
int x,y,r1,r2,maxn,minn,t,i;
bool f;
t=0;
while ((x!=-1)||(t==0))
 {
 scanf("%d%d",&x,&y);
 if (x==-1) break;
 t++;
 f=true;
 memset(use,false,sizeof(use));
 for (i=1;i<=100000;++i)
   father[i]=i;
 maxn=0; minn=100000;
   while (x!=0)
     {
       r1=find(x); use[x]=true;
       r2=find(y); use[y]=true;
       maxn=max(maxn,max(x,y));
       minn=min(minn,min(x,y));
       if (r1!=r2)
         father[r2]=r1;
else
 f=false;
scanf("%d%d",&x,&y);
     }
   root=find(minn);
   cout<<root<<endl;
   for (i=minn+1;i<=maxn;++i)
     if ((find(i)!=root)&&(use[i]))
       {f=false; break;}
   if (f)
     cout<<"Case "<<t<<" is a tree."<<endl;
   else
     cout<<"Case "<<t<<" is not a tree."<<endl;
      }
} 
2.POJ  1611 The Suspects
      大致题意:一群学生聚成一团一团(一个学生可能在不同的团中),0号学生得了非典,输出几个学生得了非典(多组数据)。
      简单并查及操作,左后扫一遍看几个学生在0号元素所在集合。
code:
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int father[30001],n,q,m;
int find(int x)
{
if (x!=father[x])
 father[x]=find(father[x]);
return father[x];
}
int main()
{
int j,i,r1,r2,x,y,root,ans;
scanf("%d%d",&n,&q);
while (n!=0)
 {
 for (i=0;i<=n;++i)
   father[i]=i;
 for (j=1;j<=q;++j)
   {
       scanf("%d%d",&m,&x);
       r1=find(x);
   for (i=2;i<=m;++i)
     {
       scanf("%d",&y);
       r2=find(y);
       if (r1!=r2)
         father[r2]=r1;
     }    
     }
   ans=0;
   root=find(0);
   for (i=0;i<=n;++i)
     if (find(i)==root)
       ans++;
   cout<<ans<<endl;
   scanf("%d%d",&n,&q);
      }
} 
3 POJ 2524 Ubiquitous Religions 
 大致题意   输入n个点之间的关系,最后输出有几个集合(多组数据)。
 初始化集合数为结点数,每成功合并一次集合数就减一,最后输出即可。
code:
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int n,q,father[50001];
int find(int x)
{
if (x!=father[x])
 father[x]=find(father[x]);
    return father[x];
}
int main()
{
int i,r1,r2,x,y,ans,t;
t=0;
scanf("%d%d",&n,&q);
while (n!=0)
 {
 t++;
 ans=n;
   for (i=1;i<=n;++i)
     father[i]=i;
   for (i=1;i<=q;++i)
     {
       scanf("%d%d",&x,&y);
       r1=find(x);
       r2=find(y);
       if (r1!=r2)
         {
           ans--;
           father[r1]=r2;
         }
     }
   cout<<"Case "<<t<<": "<<ans<<endl;
   scanf("%d%d",&n,&q);
 }
} 
4.POJ 2236 Wireless Network
 大致题意:东南亚大地震(神题意),电脑坏了,需要一台一台修,修好后可与一定欧拉距离D联网,询问i,j之间是否联网;多组数据
  很裸的思路,每修好一个电脑与其距离小于D的且修好的电脑合并即可。
code:
 #include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
bool rep[1002];
int father[1002],dis[1002][2],n,p;
int ola(int x,int y)
{
return (dis[x][0]-dis[y][0])*(dis[x][0]-dis[y][0])+(dis[x][1]-dis[y][1])
      *(dis[x][1]-dis[y][1]);
}
int find(int x)
{
if (x!=father[x])
 father[x]=find(father[x]);
return father[x];
}
int main()
{
int i,x,y,r1,r2;
char ch;
memset(rep,false,sizeof(rep));
scanf("%d%d",&n,&p);
for (i=1;i<=n;++i)
 father[i]=i;
for (i=1;i<=n;++i)
 scanf("%d%d",&dis[i][0],&dis[i][1]);
while (scanf("%*c%c",&ch)==1)
 {
   if (ch=='O')
     {
       scanf("%d",&x);
       rep[x]=true;
       for (i=1;i<=n;++i)
         if ((ola(x,i)<=p*p)&&(rep[i]))
           {
             r1=find(x);
             r2=find(i);
             if  (r1!=r2)
               father[r1]=r2;
           }
     }
   if (ch=='S')
     {
       scanf("%d%d",&x,&y);
       r1=find(x);
       r2=find(y);
       if (r1!=r2)
         printf("FAIL\n");
       else
         printf("SUCCESS\n");
     }
 }
}
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Normal:
    1.POJ 1182 食物链
     题意不说了,有中文版(好亲切),也算是并查集经典题之一吧,网上各种神牛题解,学会了其中一种(不喜勿喷):
     扩展节点法:扩展节点数n变为3*n,a存储其本身,a+n存储a吃的东西,a+2*n存储吃a的东西;
     节点大于n好判断,现在分析下两种情况
     当输入为两种为同类时,判断两者是否存在吃与不吃关系即 find(y)!=find(x+n)&&find(y)!=find(x+2*n);
      合并时三组合并 father[find(x)]=find(y); father[find(x+n)]=find(y+n); father[find(x+2*n)]=find(y+2*n);
     当输入为两种为x吃y关系,判断两者是否存在其他关系时即find(y)!=find(x+2*n)&&find(y)!=find(x);
      合并时依旧为三组 father[find(x+n)]=find(y); father[find(x)]=find(y+2*n); father[find(x+2*n)]=find(y+n);
  code:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,k,father[150001];
int find(int x)
{
if (father[x]!=x)
 father[x]=find(father[x]);
return father[x];
}
int main()
{
int i,kind,x,y,ans,r1,r2,r3,r4,r5,r6;
scanf("%d%d",&n,&k);
for (i=1;i<=3*n;++i)
 father[i]=i;
ans=0;
for (i=1;i<=k;++i)
 {
   scanf("%d%d%d",&kind,&x,&y);
   if ((x>n)||(y>n))
     {ans++; continue;}
   r1=find(x);
   r2=find(y);
   r3=find(x+n);
   r4=find(y+n);
   r5=find(x+2*n);
   r6=find(y+2*n);
   if (kind==1)
     {
       if ((r2==r3)||(r2==r5))
         {
           ans++;
           continue;
         }
       else
         {
           father[r1]=r2;
           father[r3]=r4;
           father[r5]=r6;
         }
     }
   if (kind==2)
     {
       if ((r1==r2)||(r5==r2))
         {
           ans++;
           continue;
         }
       else
         {
           father[r3]=r2;
           father[r5]=r4;
           father[r1]=r6;
         }
     }
 }
    printf("%d\n",ans);
} 
 2.POJ 1703 Find them, Catch them
 大致题意 输入n个节点,给出两者的敌人关系,左后判断两者是否为同一集合抑或无法判断(多组数据)
 建一e数组,存储与i为敌人的集合代表,每读入一组数据,合并e[i]与j、e[j]与i即可,判断两点关系时,判断两点是否在同一集合,
  若不在,则判断是否在其敌人集合,若依旧不在,则无法判断。
code:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int father[100001],gang[100001],t,n,m;
int find(int x)
{
if (x!=father[x])
 father[x]=find(father[x]);
return father[x];
}
int main()
{
int i,j,x,y,r1,r2;
char kind;
scanf("%d",&t);
for (i=1;i<=t;++i)
 {
   scanf("%d%d",&n,&m);
   for (j=0;j<=n;++j)
     father[j]=j;
   memset(gang,0,sizeof(gang));
   for (j=1;j<=m;++j)
     {
       scanf("%*c%c",&kind);
       scanf("%d%d",&x,&y);
       r1=find(x);
       r2=find(y);
       if (kind=='D')
         if (r1!=r2)
           {
             if (gang[r1]==0)
               gang[r1]=r2;
             else
               if (find(gang[r1])!=r2)
                 father[find(gang[r1])]=r2;
             if (gang[r2]==0)
               gang[r2]=r1;
             else
               if (find(gang[r2])!=r1)
                 father[find(gang[r2])]=r1;
           }
       if (kind=='A')
         {
           if (r1==r2)
             cout<<"In the same gang."<<endl;
           if (r1!=r2)
             {
               if (find(gang[r1])!=r2)
                 cout<<"Not sure yet."<<endl;
               else
                 cout<<"In different gangs."<<endl;
             }
         }
     }
 }
}
3. POJ 2492 A Bug's Life
 大致题意 给出n组昆虫关系(是否可交配),若出现矛盾则输出有同性恋者(严重吐槽此题题意)(多组数据);
 一个看似高大上的题意实际与上一题一模一样,均为合并e[i]与j、e[j]和i,最后判断即可;
code:
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int t,father[2001],e[2001],n,q;
int find(int x)
{
if (x!=father[x])
 father[x]=find(father[x]);
return father[x];
}
int main()
{
int i,j,r1,r2,r3,x,y;
bool f;
scanf("%d",&t);
for (i=1;i<=t;++i)
 {
   scanf("%d%d",&n,&q);
   memset(e,0,sizeof(e));
   for (j=1;j<=n;++j)
     father[j]=j;
   f=true;
   for (j=1;j<=q;++j)
     {
       scanf("%d%d",&x,&y);
       r1=find(x); r2=find(y);
       if (r1==r2)
         f=false;
            else
              {
                if (e[r1]==0)
 e[r1]=r2;
else
 {
   r3=find(e[r1]);
   if (r2!=r3)
     father[r2]=r3;
 }    
if (e[r2]==0)
 e[r2]=r1;
else
 {
   r3=find(e[r2]);
   if (r1!=r3)
     father[r1]=r3;
 }  
              }
     }
   if (f)
     {
      cout<<"Scenario #"<<i<<':'<<endl;
            cout<<"No suspicious bugs found!"<<endl<<endl;
     }
   else
     {
       cout<<"Scenario #"<<i<<':'<<endl;
            cout<<"Suspicious bugs found!"<<endl<<endl;
     }
 }
}
4.POJ 1733 Parity game(codevs 奇偶游戏,tyvj和vijos 小胖的奇偶)
 大致题意,直接上codevs的题面:
你和你的朋友玩一个游戏。你的朋友写下来一连串的0或者1。你选择一个连续的子序列然后问他,这个子序列包含1的个数是
奇数还是偶数。你的朋友回答完你的问题,接着你问下一个问题。 你怀疑你朋友的一些答案可能是错误的,你决定写一个程序来帮忙。程序将接受一系列你的问题及你朋友的回答,程序的目的 是找到第一个错误的回答i,也就是存在一个序列满足前i-1个问题的答案,但是不满足前i个问题。 
与银河英雄传说想法相似,但略简单,pa[i]存储i到其目前根结点距离(即为是奇是偶)(路径压缩亦可理解为其与父亲的距离),最后判断是否符合该条语句条件即可,数据范围较大,需离散化 
code:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
long long l,n;
int father[10001],pa[10001]={0};
long long a[10001],b[10001],kind[5001];
int find(int x)
{
int j;
if (x!=father[x])
 {
   j=find(father[x]);
pa[x]+=pa[father[x]]; //更新距离;
father[x]=j;  
 }
return 
 father[x];
}
int main()
{
int i,x,y,r1,r2,size;
bool f=false;
char s[10];
scanf("%lld%d",&l,&n);
    for (i=1;i<=n;++i)
      {
        scanf("%lld%lld",&a[i*2-1],&a[i*2]);
        a[i*2-1]--;
        b[i*2-1]=a[i*2-1]; b[i*2]=a[i*2];
        scanf("%s",&s);
        if (s[0]=='e')
          kind[i]=0;
        if (s[0]=='o')
          kind[i]=1;
      }
    sort(b+1,b+2*n+1);
    size=unique(b+1,b+2*n+1)-b-1;
    for (i=1;i<=2*n;++i)
      a[i]=upper_bound(b+1,b+size+1,a[i])-b-1;// 这之上为离散化
    for (i=1;i<=size;++i)
      father[i]=i;
    for (i=1;i<=n;++i)
      {
        x=a[2*i-1]; y=a[2*i];
   r1=find(x); r2=find(y);
if (r1!=r2)
 {
   father[r2]=r1;
pa[r2]=pa[x]+kind[i]-pa[y];  //关键语句
     }
   if (r1==r2)
     {
       if (abs(pa[y]-pa[x])%2!=kind[i])
         {
           cout<<i-1<<endl;
           f=true;
           break;
         }
     }
      }
    if (f==false)
      cout<<n<<endl;
} 
5.POJ 1984 Navigation Nightmare
 大致题意:给出一个节点与另一个节点相对位置关系,最后输完在输入询问(时间即为第几条语句),输出两点之间曼哈顿距离(未联通输出-1)。
 想法与上题基本类似,但是pa[i]存储其与其父亲的曼哈顿距离,公式较难推出;(ps:离线处理太麻烦了)。
code:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
long long l,n;
int father[10001],pa[10001]={0};
long long a[10001],b[10001],kind[5001];
int find(int x)
{
int j;
if (x!=father[x])
 {
   j=find(father[x]);
pa[x]+=pa[father[x]]; //更新距离;
father[x]=j;  
 }
return 
 father[x];
}
int main()
{
int i,x,y,r1,r2,size;
bool f=false;
char s[10];
scanf("%lld%d",&l,&n);
    for (i=1;i<=n;++i)
      {
        scanf("%lld%lld",&a[i*2-1],&a[i*2]);
        a[i*2-1]--;
        b[i*2-1]=a[i*2-1]; b[i*2]=a[i*2];
        scanf("%s",&s);
        if (s[0]=='e')
          kind[i]=0;
        if (s[0]=='o')
          kind[i]=1;
      }
    sort(b+1,b+2*n+1);
    size=unique(b+1,b+2*n+1)-b-1;
    for (i=1;i<=2*n;++i)
      a[i]=upper_bound(b+1,b+size+1,a[i])-b-1;// 这之上为离散化
    for (i=1;i<=size;++i)
      father[i]=i;
    for (i=1;i<=n;++i)
      {
        x=a[2*i-1]; y=a[2*i];
   r1=find(x); r2=find(y);
if (r1!=r2)
 {
   father[r2]=r1;
pa[r2]=pa[x]+kind[i]-pa[y];  //关键语句
     }
   if (r1==r2)
     {
       if (abs(pa[y]-pa[x])%2!=kind[i])
         {
           cout<<i-1<<endl;
           f=true;
           break;
         }
     }
      }
    if (f==false)
      cout<<n<<endl;
} 
  6.poj 1988 Cube Stacking
  题意 几乎与银河英雄传说一样,飞船变成盒子之类的东西;
  son[i]存储以i为根有几个孩子,pa[i]存储其到父亲的距离,不断维护即可;(这道题与前两道题模版几乎一样);
code:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int father[30001],son[30001],n,pa[30001];
int find(int x)
{
int j;
if (x!=father[x])
 {
   j=find(father[x]);
   pa[x]=pa[father[x]]+pa[x]; //维护核心代码,由于路径压缩,其到父亲距离不一定为1;
   father[x]=j;
      }
return father[x];
}
int main()
{
int i,x,y,r1,r2,r;
char ch;
for (i=1;i<=30000;++i)
 {
        father[i]=i;
        son[i]=1;
        pa[i]=0;
      }
    scanf("%d",&n);
    for (i=1;i<=n;++i)
      {
        scanf("%*c%c",&ch);
        if (ch=='M')
          {
            scanf("%d%d",&x,&y);
            r1=find(x);
            r2=find(y);
            pa[r2]=son[r1];//当前要合并节点到父亲距离即为son个数;
            son[r1]=son[r2]+son[r1]; //更新孩子数信息;
            father[r2]=r1;
          }
        if (ch=='C')
          {
          scanf("%d",&x);
            r=find(x);
            cout<<son[r]-pa[x]-1<<endl;
          }
      }
} 
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Hard:
  唯一一道 POJ 2546 Rochambeau
  大致题意 有一群小朋友玩剪子石头布,分成3组,每一组都只能出一个手势,但有一个是裁判, 可以任意手势(这是裁判。。。),最后输处在第几条语句得出谁为裁判,可能有多个裁判时,输出 Can not determine,可以无裁判时,输出 Impossible;
易想出穷举裁判是谁,排除裁判后按食物链做即可 ,若出现矛盾则此人不可能为裁判,判断某人不可能为裁判的最大语句数即为判断谁是裁判的步数(排除最后另外一人为裁判的可能性),想出算法code呼之欲出;
code:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
struct hp{
int x,y;
char kind;
}a[2001];
int father[1501],n,m;
int find(int x)
{
if (x!=father[x])
 father[x]=find(father[x]);
return father[x];
}
int main()
{
int i,j,jdg,r1,r2,r3,r4,r5,r6,x,y,maxn,l,k,ans,kind;
char set[1000];
bool f;
while (scanf("%d%d",&n,&m)==2)
 {
   if ((n==1)&&(m==0)) 
 cout<<"Player 0 can be determined to be the judge after 0 lines"<<endl;
else
 {
 ans=0; maxn=0; 
 memset(a,0,sizeof(a));
 for (j=1;j<=m;++j)
     {
       scanf("%s",&set);
       l=strlen(set);
       k=0;
       x=0; y=0;
       while (set[k]>='0'&&set[k]<='9')
         {
           x=x*10+(int)(set[k])-48;
           k++;
         }
       kind=set[k];
       k++;
       while (k<l)
         {
                 y=y*10+(int)(set[k])-48;
           k++;
         }
       a[j].x=x; a[j].kind=kind; a[j].y=y;
     }
   for (i=0;i<=n-1;++i)
     {
     f=true; 
       for (j=0;j<=3*n;++j)
         father[j]=j;
       for (j=1;j<=m;++j)
         {
           x=a[j].x; y=a[j].y; kind=a[j].kind;
           if ((x!=i)&&(y!=i))
             {
             if (kind=='<')
              {
                swap(x,y);
                kind='>';
              }
               r1=find(x);
               r2=find(y);
               r3=find(x+n);
               r4=find(y+n);
               r5=find(x+2*n);
               r6=find(y+2*n);
               if (kind=='=')
                 {
                   if ((r2==r3)||(r2==r5))
                     {
                       maxn=max(maxn,j);
                       f=false;
                       break;
                     }
                   else
 {
   father[r2]=r1;
   father[r3]=r4;
   father
[r5]=r6;
 }  
     }
               if (kind=='>')
 {
   if ((r2==r5)||(r1==r2))
     {
       maxn=max(maxn,j);
       f=false;
       break;
     }
   else
     {
       father[r3]=r2;
       father[r5]=r4;
       father[r1]=r6;
     }
 } 
             }
         }
       if (f)
         {
         if (ans>0)
             {
               cout<<"Can not determine"<<endl;
               ans++;
               break;
             }
           if (ans==0)
             {ans++; jdg=i;} 
         }
     }
   if (ans==1)
     cout<<"Player "<<jdg<<" can be determined to be the judge after "<<maxn<<" lines"<<endl;
   if (ans==0)
     cout<<"Impossible"<<endl;
 }
 } 
}

 
-------------------------------------------------------------------------------------------------------------------------------
感谢同机房神牛TA,Rivendile,yangfangyuan;
                                                                                                         lcomyn
                                                                                                        2014.10.19 

你可能感兴趣的:(POJ 2014.10.19 并查集刷题记录)