2014级寒假特训之并查集专题


Problem A: Double和XXZ的生日宴请

Time Limit: 1 Sec   Memory Limit: 128 MB
Submit: 9   Solved: 7
[ Submit][ Status][ Web Board] [ Edit] [ TestData]

Description

Double 和 XXZ同一天生日,他们俩30岁生日那天,当年ACM集训队的好友,高富帅RMD,SXK,Tyh,,Ljy,Lhr,Wdm,SHF,Lsj,Kyd.....白富美Lwq...等特地从西雅图,迪拜,香港...飞到北京给两位好友庆生,Double和XXZ那天晚上特别兴奋,他俩商量准备在盘古七星酒店宴请各位好友,但是现在问题来了,很多Double的好友,XXZ不怎么认识,很多XXZ的好友Double不怎么认识(当然昔日集训队的好友当然都是互相很熟的啦),不能让不认识的两个人做在一桌,那样缺少气氛,也会很尴尬,但是朋友的朋友可以坐在一桌,比如tyh的wife不认识KYD,但是KYD和TYH是好友,那么他们俩可以做一桌,笨笨的Double和XXZ不知道该订多少桌好,定多了吧,坐位显的特别空,没气氛;定少了吧,让两个不能通过中间朋友认识的人坐一桌也不好;现在请帮助这两位解决这个问题。为了简化问题,我们将double和XXZ的每个好友都编号1,2,3....N(1<=N<=10000)

Input

第一行T(1<=T<=30),代表有多少组测试样例
第二行N,M分别代表朋友的总数,有多少组朋友关系
接下来M行:每行两个数X,Y代表X和Y认识

Output

每组测试样例,输出要安排多少个桌子

Sample Input

2 
5 3 
1 2 
2 3 
4 5 

5 1 
2 5

Sample Output

2
4

HINT



第一个样例:5个人,3种关系

1和2是朋友,2和3是朋友,那么1,2,3一桌

4和5是朋友,那么4和5坐一桌,总共5个人坐两桌就可以


直接套模板就可以:

#include <stdio.h>
#include <time.h>
#include <iostream>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int maxn = 1e5+10;
int father[maxn];
 
void init()
{
    for(int i = 1 ; i <= maxn; i++)  father[i] = i;
}
 
int Find(int x)
{
    return x == father[x] ?  x : father[x] = Find(father[x]);
}
 
void Union(int x, int y)
{
    int fx = Find(x);
    int fy = Find(y);
    if(fx != fy)
    {
        father[fy] = fx;
    }
}
 
int main()
{
    #ifdef xxz
    freopen("out.txt","w",stdout);
    freopen("in.txt","r",stdin);
    #endif // xxz
    int Case,x,y,n,m,count;
    scanf("%d",&Case);
    while(Case--)
    {
        count = 0;
        scanf("%d%d",&n,&m);
        init();
        for(int i = 0; i < m; i++)
        {
            scanf("%d%d",&x,&y);
            Union(x,y);
        }
 
        for(int i = 1 ; i<= n; i++)
        {
            if(father[i] == i) count++;
 
        }
        printf("%d\n",count);
    }
    return 0;
}


Problem B: Harbin

Time Limit: 1 Sec   Memory Limit: 128 MB
Submit: 2   Solved: 2
[ Submit][ Status][ Web Board] [ Edit] [ TestData]

Description

Ben La Deng is a really bad guy. He destroys everything he met. 
One day Ben La Deng went to Harbin .Harbin has N points and M lines. Each line connects exactly two points. Ben La Deng will destroy all the lines. The mayor of Harbin wants to know how many connected blocks of Harbin left after Ben La Deng destroying the first K lines in the input. 
Two points are in the same connected blocks if and only if they connect to each other directly or indirectly.

Input

First line of the input contains two integers N and M. 
Then following M lines each containing 2 space-separated integers u and v, which denotes an line. 
Constraints: 
0 < N <= 10000 
0 < M <= 100000 
0 <= u, v < N. 

Output

Output M lines, the ith line is the answer after deleting the first i edges in the input.

Sample Input

5 10
0 1 
1 2 
1 3 
1 4 
0 2 
2 3 
0 4 
0 3 
3 4 
2 4

Sample Output

1
1
1
2
2
2
2
3
4
5

HINT

 he graph given in sample input is a complete graph, that each pair of vertex has an edge connecting them, so there's only 1 connected block at first. The first 3 lines of output are 1s because after deleting the first 3 edges of the graph, all vertexes still connected together. But after deleting the first 4 edges of the graph, vertex 1 will be disconnected with other vertex, and it became an independent connected block. Continue deleting edges the disconnected blocks increased and finally it will became the number of vertex, so the last output should always be N.

[ Submit][ Status][ Web Board] [ Edit] [ TestData]
这道题目的意思就是,不断的删除边之后,剩下多少个互不相连的集合

比如删除0 1这条边以后还是1个集合

........慢慢删

思路就是倒着增加边,比如最后一个是删除2,4,那么我们就将2,4这条边第一个连起来,看连起来以后有多少个集合

继续连3,4这条边,看连起来以后有多少个集合

最后再连0,1

逆向思维求解

#include <stdio.h>
const int maxn = 1e5+10;
int father[maxn];
int N,M;
int ans[maxn];
 
struct B
{
    int u,v;
}bian[maxn];
 
void init()
{
    for(int i = 0; i <= N; i++)
    {
        father[i] = i;
    }
}
 
int Find(int x)
{
    return x == father[x] ? x : father[x] = Find(father[x]);
}
 
void Union(int x,int y)
{
    int fx = Find(x);
    int fy = Find(y);
    if(fx != fy)
    {
        father[fy] = fx;
    }
}
 
int main()
{
    while(scanf("%d%d",&N,&M) != EOF)
    {
        init();
        int cent = N;
        for(int i = 0; i < M; i++)
        {
            scanf("%d%d",&bian[i].u,&bian[i].v);
        }
 
       int p = 0;
       for(int i = M-1; i >= 0; i--)
       {
 
           if(Find(bian[i].u) != Find(bian[i].v) )
           {
               ans[p++] = cent--;
               Union(bian[i].u,bian[i].v);
           }
           else ans[p++] = cent;
 
       }
 
       for(int i = p-1; i >= 0; i--)
       {
           printf("%d\n",ans[i]);
       }
 
    }
    return 0;
}



Problem C: Is It A Tree?

Time Limit: 1 Sec   Memory Limit: 128 MB
Submit: 2   Solved: 2
[ Submit][ Status][ Web Board] [ Edit] [ TestData]

Description

A tree is a well-known data structure that is either empty (null, void, nothing) or is a set of one or more nodes connected by directed edges between nodes satisfying the following properties. 

There is exactly one node, called the root, to which no directed edges point. 
Every node except the root has exactly one edge pointing to it. 
There is a unique sequence of directed edges from the root to each node. 
For example, consider the illustrations below, in which nodes are represented by circles and edges are represented by lines with arrowheads. The first two of these are trees, but the last is not. 


In this problem you will be given several descriptions of collections of nodes connected by directed edges. For each of these you are to determine if the collection satisfies the definition of a tree or not.

Input

First line of the input contains one integers N ,
Then following Nines each containing 2 space-separated integers u and v,u is father of v

Output

For each test case display the line "Case k is a tree." or the line "Case k is not a tree.", where k corresponds to the test case number (they are sequentially numbered starting with 1).

Sample Input

5
6 8  
5 3  
5 2  
6 4  
5 6
8
8 1  
7 3  
6 2  
8 9  
7 5
7 4  
7 8  
7 6
6
3 8  
6 8  
6 4
5 3  
5 6  
5 2

Sample Output

Case 1 is a tree.
Case 2 is a tree.
Case 3 is not a tree.

判断给出的是不是一树,其实只要判断有没有环就行,如果并查集的过程中有环产生,那么一定不是树,用一个变量标记一下就行

#include <stdio.h>
const int maxn = 1e5+10;
int father[maxn];
 
void init()
{
    for(int i = 1 ; i <= maxn; i++)  father[i] = i;
}
 
int Find(int x)
{
    return x == father[x] ?  x : father[x] = Find(father[x]);
}
 
void Union(int x, int y)
{
    int fx = Find(x);
    int fy = Find(y);
    if(fx != fy)
    {
        father[fy] = fx;
    }
}
 
 
int main()
{
    int Case = 1,cent;
    while(scanf("%d",¢) != EOF)
    {
        init();
        int flag = true;
        for(int i = 0; i < cent; i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            if(Find(a) == Find(b))
            {
                flag = false;
            }
            else {
                Union(a,b);
            }
        }
 
        if(!flag)
        {
            printf("Case %d is not a tree.\n",Case++);
        }
        else  printf("Case %d is a tree.\n",Case++);
 
 
    }
    return 0;
}

Problem D: Bad Horse

Time Limit: 1 Sec   Memory Limit: 128 MB
Submit: 3   Solved: 3
[ Submit][ Status][ Web Board] [ Edit] [ TestData]

Description

As the leader of the Evil League of Evil, Bad Horse has a lot of problems to deal with.

Most recently, there have been far too many arguments and far too much backstabbing in the League, so much so that Bad Horse has decided to split the league into two departments in order to separate troublesome members.

Being the Thoroughbred of Sin, Bad Horse isn't about to spend his valuable time figuring out how to split the League members by himself.

That what he's got you -- his loyal henchman -- for.

Input

The first line of the input gives the number of test cases, T (1 ≤ T ≤ 100).

T test cases follow.

Each test case starts with a positive integer M (1 ≤ M ≤ 100) on a line by itself -- the number of troublesome pairs of League members.

The next M lines each contain a pair of names, separated by a single space.

Each member name will consist of only letters and the underscore character "_".

Names are case-sensitive.

No pair will appear more than once in the same test case.

Each pair will contain two distinct League members.

Output

For each test case, output one line containing "Case #x: y", where x is the case number (starting from 1) and y is either "Yes" or "No", depending on whether the League members mentioned in the input can be split into two groups with neither of the groups containing a troublesome pair.

Sample Input

2
1
Dead_Bowie Fake_Thomas_Jefferson
3
Dead_Bowie Fake_Thomas_Jefferson
Fake_Thomas_Jefferson Fury_Leika
Fury_Leika Dead_Bowie

Sample Output

Case #1: Yes
Case #2: No

HINT

这题有多种方法求解,现在给出种类并查集的求解方法

不过这题首先要解决的就是如何用一个数字代表一个字符串

1:可以用结构体

struct node

{

       char s[100];

      int id;

};

一个存字符串一个存数字,但要保证不同的字符串对应的数字不同,相同字符串对应的数字标号相同

2:

c++STL有个一map容器,很方便,大家可以提前了解一下,也没什么难的,知道怎么用就行了

比如声明map<string,int> p;

那么可以将“abcde”标记为1,map["abcde"] = 1,很简单的操作,可以了解下就可以用


题意就是给出的两个字符串一定不能在同一个阵营当中

现在我们把它们分为A,B两类

假设A类的标号都是1-100,B类的标号都是大于100,这样是不是很好区分,为什么100呢?因为题目给出的n的范围就是100,如果你把B小于100的话,a,b就分不清

现在如果x,y是不同正营的那么x,x+101肯定不是一伙的,那么x+101和y肯定是一伙的,同理x和y+101也是一伙的

。。。。。

如果还不懂这题直接放下吧,以后再弄

#include <iostream>
#include <map>
#include <cstdio>
#include <string>
using namespace std;
int a[501];
//题意是能不能把一组两个人分到两个不同的正营里面,关键利用map映射
void init()
{
    for(int i = 0; i <= 200; i++)
    {
        a[i] = i;
    }
}
 
int Find(int x)
{
    while(x != a[x])
        x= a[x];
    return x;
}
 
void Union(int x,int y)
{
    int fx = Find(x);
    int fy = Find(y);
 
    if(fx != fy)
    {
        a[fx] = fy;
    }
}
int main()
{
    #ifdef xxz
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
    #endif // xxz
    int t;
    cin >> t;
    for(int j = 1; j <= t; j++)
    {
        init();
        map<string,int> ma;
        int m,cnt = 1;
        cin >> m;
        bool flag = true;
        for(int i = 0; i < m ; i++)
        {
            string s1,s2;
            cin >> s1 >> s2;
            if(!ma[s1]) ma[s1] = cnt++;
            if(!ma[s2]) ma[s2] = cnt++;
 
            if(Find(ma[s1]) == Find(ma[s2]) )
            {
                flag = false;
            }
            //核心代码
            /*
                现在x和y异类,并且x和x+101异类,说明y和x+101同类;
同时x和y异类,y+101和y异类, 说明x和y+101同类。我们需要把所有同类的放到一个集合里,避免遗漏的情况。                          */
            Union(ma[s1],ma[s2] + 101);
            Union(ma[s2],ma[s1] + 101);
 
        }
        printf("Case #%d: %s\n",j,flag ? "Yes":"No");
    }
    return 0;
}

Problem E: DoubleQ的拆分集合

Time Limit: 1 Sec   Memory Limit: 128 MB
Submit: 13   Solved: 5
[ Submit][ Status][ Web Board] [ Edit] [ TestData]

Description

众所周知,DoubleQ是DS(Data Structure)粉,她最爱DS了。现在她要实现一个神奇的DS,支持下列两个操作:
-删除某条边,表示为”D x”,即为删除第x条边
-查询两点是否属于一个集合,表示为”Q a b”,即为查询节点a与节点b是否在一个集合内,若在同一个集合内,输出”Yes”,否则输出”No”(输出不包括引号)

n<=100000,m<=100000,q<=100000

Input

多组数据。

对于每组数据,第一行包括三个正整数,分别代表数据结构中节点的个数n,边数m,操作数q

接下来的m行代表第i条边(从1开始),第i条边连接u和v两个顶点(无重边,无自环)
再接下来的q行每行包括一个格式如题描述操作

Output

对于每一个查询,输出包括一行,表示查询结果,即如果两点属于同一个集合输出”Yes”,否则输出”No”。

Sample Input

5 4 6
1 2
2 3
4 5
1 3
Q 1 2
D 1
Q 1 2
Q 3 4
D 2
Q 1 2

Sample Output

Yes
Yes
No
No

这题是B题的升级题,区别就是在于你逆向添加第一条边(也就是最后一个删除操作)的时候,不是向B题那样没有联通的点,这题区别就是在于加第一条边的时候有一些点已经联通了,也就是样例中最开始给出的那些点

所以们一开始就要把这些点连起来,请看代码


[cpp]  view plain copy
  1. #include <iostream>  
  2. #include <cstdio>  
  3. #include <cstring>  
  4. using namespace std;  
  5. const int maxn = 1e5+10;  
  6. int father[maxn];  
  7. int n,m,q;  
  8. string  s[maxn];  
  9. struct B  
  10. {  
  11.     int a,b,p;  //a,b代表链接的点,p代表ab链接的那条边是否已删除,1代表没删,0代表已删除
  12. } bian[maxn];  
  13.   
  14. struct C  
  15. {  
  16.     char ch;  
  17.     int c;  
  18.     int d;  
  19. } caozuo[maxn];  
  20.   
  21. void init()  
  22. {  
  23.     for(int i = 1; i <= n; i++)  
  24.     {  
  25.         father[i] = i;  
  26.     }  
  27. }  
  28.   
  29. int Find(int x)  
  30. {  
  31.     return x == father[x] ? x : father[x] = Find(father[x]);  
  32. }  
  33.   
  34. void Union(int x,int y)  
  35. {  
  36.     int fx = Find(x);  
  37.     int fy = Find(y);  
  38.     if(fx != fy)  
  39.     {  
  40.         father[fy] = fx;  
  41.     }  
  42. }  
  43.   
  44. int main()  
  45. {  
  46.   
  47.     freopen("in.txt","r",stdin);  
  48.     freopen("out.txt","w",stdout);  
  49.     while(cin>>n>>m>>q)  
  50.     {  
  51.         init();  
  52.         for(int i = 1; i<= m; i++)  
  53.         {  
  54.             cin>>bian[i].a>>bian[i].b;  
  55.             bian[i].p = 1;  
  56.         }  
  57.   
  58.         for(int i = 1; i<= q; i++)  
  59.         {  
  60.             cin>>caozuo[i].ch;  
  61.             if(caozuo[i].ch == 'Q')  
  62.             {  
  63.                 cin>>caozuo[i].c>>caozuo[i].d;  
  64.   
  65.             }  
  66.             else  
  67.             {  
  68.                 cin>>caozuo[i].c;  
  69.                 bian[caozuo[i].c].p = 0;  
  70.   
  71.             }  
  72.         }  
  73.   
  74.         for(int i = 1; i<= m; i++)  
  75.         {  
  76.             if(bian[i].p)  
  77.             {  
  78.                 Union(bian[i].a,bian[i].b);  
  79.             }  
  80.         }  
  81.   
  82.         int cent = 0;  
  83.   
  84.         for(int i = q; i>= 1; i--)  
  85.         {  
  86.   
  87.   
  88.             if(caozuo[i].ch == 'Q')  
  89.             {  
  90.                 if(Find(caozuo[i].c) == Find(caozuo[i].d))  
  91.                 {  
  92.   
  93.                     s[cent++] = "YES";  
  94.                 }  
  95.                 else  
  96.                 {  
  97.                     s[cent++] = "NO";  
  98.                 }  
  99.             }  
  100.             else  
  101.             {  
  102.                 int temp = caozuo[i].c;  
  103.                 Union(bian[temp].a,bian[temp].b);  
  104.             }  
  105.         }  
  106.   
  107.         for(int i = cent-1; i >= 0; i--)  
  108.         {  
  109.             cout<<s[i]<<endl;  
  110.         }  
  111.   
  112.     }  
  113.     return 0;  
  114. }  
然后这题的原创作者是北航的董适大神(人在微软总部),哈工大的xiaodao验题,下面贴上适牛的标程(果然比我写的短好多,ym!!!)

说明一下,struct在c++里面可以是一个类,所以里面可以添加函数,然后bool类型的返回值要么是1,要么是0

[cpp]  view plain copy
  1. #include <cstdio>  
  2. #include <cstring>  
  3.   
  4. struct road  
  5. {  
  6.     int u,v;  
  7.     bool p;  
  8.     void insert()  
  9.     {  
  10.         scanf("%d%d",&u,&v);  
  11.         p=1;  
  12.     }  
  13. }f[100005];  
  14.   
  15. int n,m,q,g[100005][3],set[100005];  
  16. bool ans[100005];  
  17. char s[10];  
  18.   
  19. int find(int x)  
  20. {  
  21.     return x==set[x]?x:(set[x]=find(set[x]));  
  22. }  
  23.   
  24. int main()  
  25. {  
  26.     freopen("cut.in","r",stdin);  
  27.     freopen("cut.out","w",stdout);  
  28.     while(~scanf("%d%d%d",&n,&m,&q)){  
  29.         for(int i=1;i<=m;i++)  
  30.             f[i].insert();  
  31.         for(int i=1;i<=n;i++)  
  32.             set[i]=i;  
  33.         for(int i=1;i<=q;i++)  
  34.         {  
  35.             scanf("%s",s);  
  36.             if(s[0]=='D')  
  37.             {  
  38.                 g[i][0]=0;  
  39.                 scanf("%d",&g[i][1]);  
  40.                 f[g[i][1]].p=0;  
  41.             }  
  42.             else  
  43.             {  
  44.                 g[i][0]=1;  
  45.                 scanf("%d%d",&g[i][1],&g[i][2]);  
  46.             }  
  47.         }  
  48.         for(int i=1;i<=m;i++)  
  49.             if(f[i].p)set[find(f[i].u)]=find(f[i].v);  
  50.         for(int i=q;i>0;i--)  
  51.         {  
  52.             if(g[i][0])ans[i]=(find(g[i][1])==find(g[i][2]));  
  53.             else set[find(f[g[i][1]].u)]=find(f[g[i][1]].v);  
  54.         }  
  55.         for(int i=1;i<=q;i++)  
  56.             if(g[i][0])  
  57.             {  
  58.                 if(ans[i])puts("Yes");  
  59.                 else puts("No");  
  60.             }  
  61.     }  
  62.     return 0;  
  63. }  

然后这些天辛苦大家了,希望你们能学到东西,还有你们可能和我当初进组一样,学ACM以后到底干什么?我没有项目经验怎么办?(不吹牛,acm组是科协就业率和薪资最高的组,但那些去阿里和百度的学长学姐也没有项目经验,最拿手的就是数据结构和算法),还有是不是不拿奖就去不了大公司呢?不不不,08级的苗松学长也没拿过亚洲赛的奖,照样凭借深厚的基础知识(算法和数据结构功底)进了阿里巴巴20W年薪,顺便带他girl friend也进阿里10w年薪.......所以大家不用担心没有项目经验了,好好A题,

如果还有疑惑请看这篇文章http://blog.csdn.net/u013445530/article/details/42500715

大家加油呀,弱弱的学长只能教你们这些了,剩下的路就靠你们自己走了。。。。不懂的就要问,不要害羞!

最后祝大家新年快乐!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值