并查集的几道题目

先从最简单的开始:

题意: 一共n个强盗,m条线索,输出有多少个独立团伙(同伙的同伙是同伙,老鼠的儿子会打洞)

样例输入:

10 9

1 2

3 4

5 2

4 6

2 6

8 7

9 7

1 6

2 4

样例输出:

3

代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//#define ONLINE_JUDGE
#define eps 1e-8
#define INF 0x7fffffff                                            //INT_MAX
#define inf 0x3f3f3f3f                                            //int??????????????????
#define FOR(i,a) for((i)=0;i<(a);(i)++)                            //[i,a);
#define MEM(a) (memset((a),0,sizeof(a)))
#define sfs(a) scanf("%s",a)
#define sf(a) scanf("%d",&a)
#define sfI(a) scanf("%I64d",&a)
#define pf(a) printf("%d\n",a)
#define pfI(a) printf("%I64d\n",a)
#define pfs(a) printf("%s\n",a)
#define sfd(a,b) scanf("%d%d",&a,&b)
#define sft(a,b,c)scanf("%d%d%d",&a,&b,&c)
#define for1(i,a,b) for(int i=(a);i=a;i--)
#define MEM1(a) memset(a,0,sizeof(a))
#define MEM2(a) memset(a,-1,sizeof(a))
#define LL __int64
const double PI = acos(-1.0);
template T gcd(T a, T b) {return b ? gcd(b, a % b) : a;}
template T lcm(T a, T b) {return a / gcd(a, b) * b;}
template inline T Min(T a, T b) {return a < b ? a : b;}
template inline T Max(T a, T b) {return a > b ? a : b;}
using namespace std;
int n,m;
#define N 1010
int robber[N];
void Init(){						//初始化函数
	for(int i = 1; i <= n; i++){
		robber[i] = i;
	}
}
int getheader(int u){					//找爹函数
	if(robber[u] == u){
		return u;
	}
	else{
		return robber[u] = getheader(robber[u]);
	}
}
void merge(int u,int v){				//合并u,v强盗团伙的函数
	int t1,t2;
	t1 = getheader(u);
	t2 = getheader(v);
	if(t1 > t2){					//让标号比较大的强盗都归顺编号比较小的强盗
		t1 ^= t2;
		t2 ^= t1;
		t1 ^= t2;
	}
	if(t1 != t2){
		robber	[t2] = t1;
	}
}
int main(){
#ifndef ONLINE_JUDGE
//    freopen("in.txt","r",stdin);
//    freopen("out.txt","w",stdout);
#endif
	while(sfd(n,m) != EOF){
		Init();
		int r1,r2;
		for(int i = 0; i < m;i++){
			sfd(r1,r2);
			merge(r1,r2);
		}
		int num = 0;
		for(int i = 1; i <= n; i++){
			if(robber[i] == i)
				num++;
		}
		pf(num);
	}
    return 0;
}

没啥好总结的。。。

然后有变形。。。又是王教授出的。。

找朋友

时间限制(C/C++):1000MS/3000MS          运行内存限制:65536KByte
总提交:422            测试通过:138

描述

假设朋友关系是可以传递的,也就是说,如果A和B是朋友,B和C是朋友,那么A和C也是朋友。给出一个人员集合及其朋友关系,则这个集合可以分割成为不相交的子集合。给出一个人员的集合,以及他们之间部分的关系(哪两个人是朋友),让你求出分割出的子集合中最大的那个有多少人。

输入

第一行是一个整数,在[2,50]之间,表示要输入的案例的数量。对每个案例,第一行是2个整数,用空格隔开。第一个整数m表示集合的规模,第二个整数给出的关系数量n。后面紧跟n行,每一行都是两个整数x和y,满足0 <= x, y < n。0

输出

对每一个案例,输出该集合分割后最大的那个子集合中的人数。

样例输入

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

样例输出

4
3
这下好,还要统计人数。。用个结构体就ok

代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//#define ONLINE_JUDGE
#define eps 1e-8
#define INF 0x7fffffff                                            //INT_MAX
#define inf 0x3f3f3f3f                                            //int??????????????????
#define FOR(i,a) for((i)=0;i<(a);(i)++)                            //[i,a);
#define MEM(a) (memset((a),0,sizeof(a)))
#define sfs(a) scanf("%s",a)
#define sf(a) scanf("%d",&a)
#define sfI(a) scanf("%I64d",&a)
#define pf(a) printf("%d\n",a)
#define pfI(a) printf("%I64d\n",a)
#define pfs(a) printf("%s\n",a)
#define sfd(a,b) scanf("%d%d",&a,&b)
#define sft(a,b,c)scanf("%d%d%d",&a,&b,&c)
#define for1(i,a,b) for(int i=(a);i=a;i--)
#define MEM1(a) memset(a,0,sizeof(a))
#define MEM2(a) memset(a,-1,sizeof(a))
#define LL __int64
const double PI = acos(-1.0);
template T gcd(T a, T b) {return b ? gcd(b, a % b) : a;}
template T lcm(T a, T b) {return a / gcd(a, b) * b;}
template inline T Min(T a, T b) {return a < b ? a : b;}
template inline T Max(T a, T b) {return a > b ? a : b;}
using namespace std;
int n,m;
#define N 105
struct People{
    int header;
    int num;
};
People a[N];
void My_swap(int & a,int & b){
    a ^= b;     b ^= a;     a ^= b;
}
void Init(){
    for(int i = 0; i < n;i++){
        a[i].header = i;
        a[i].num = 1;
    }
}
int getheader(int t){
    if(a[t].header == t){
        return t;
    }
    else{
        return a[t].header = getheader(a[t].header);
    }
}
void merge(int u,int v){
    int t1,t2;
    t1 = getheader(u);
    t2 = getheader(v);
    if(t1 > t2){
        My_swap(t1,t2);				//其实这步并没有什么必要,好理解一点点
    }
    if(t1 != t2){
        a[t2].header = t1;
        a[t1].num += a[t2].num;
    }
}
int main(){
#ifndef ONLINE_JUDGE
//    freopen("in.txt","r",stdin);
//    freopen("out.txt","w",stdout);
#endif
    int t,x,y;
    sf(t);
    while(t--){
        sfd(n,m);
        Init();
        for(int i = 0; i < m; i++){
            sfd(x,y);
            merge(x,y);
        }
        int max_sum = 0;
        for(int i = 0; i < n; i++){
            max_sum = max_sum > a[i].num ? max_sum : a[i].num;
        }
        pf(max_sum);
    }
    return 0;
}
只是跑得慢了点。。。

还有一道变了变样子的题:

AQ and his xiaoqiang

时间限制(C/C++):1000MS/3000MS          运行内存限制:65536KByte
总提交:45            测试通过:8

描述

In AQ's dormitory,there are n xiaoqiang.They are so smart that they have their ways to know others as friends or enemies,
any two of them are friends or enemies if they know each other.And the relationship among them described as follows:
(we assume 3 xiaoqiang named A,B,C)
1.if A's friend is B,B's friend is C,so C is also a friend of A.
2.if A's enemy is B,B's enemy is C,so C is a friend of A.
We know a family consist of the xiaoqiang who are all friends between any two of them.So if I tell you 
m relationships among n xiaoqiang,can you tell me what is the maximum number of families in AQ's dormitory?

输入

There are multiple cases.In the first line,there is n and m,represents the number of xiaoqiang and the relationships.(n<1000, m<5000)
among them.In the following m rows,there are three numbers:p x y.
1.p=1,x and y are friends.
2.p=0,x and y are enemies.



输出

Please output the maximum number of families in one line.

样例输入

6 4
0 1 4
1 3 5
1 4 6
0 1 2

样例输出

3

居然是小强我也是醉了,还有敌人啊朋友我也是醉了

代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//#define ONLINE_JUDGE
#define eps 1e-8
#define INF 0x7fffffff                                            //INT_MAX
#define inf 0x3f3f3f3f                                            //int??????????????????
#define FOR(i,a) for((i)=0;i<(a);(i)++)                            //[i,a);
#define MEM(a) (memset((a),0,sizeof(a)))
#define sfs(a) scanf("%s",a)
#define sf(a) scanf("%d",&a)
#define sfI(a) scanf("%I64d",&a)
#define pf(a) printf("%d\n",a)
#define pfI(a) printf("%I64d\n",a)
#define pfs(a) printf("%s\n",a)
#define sfd(a,b) scanf("%d%d",&a,&b)
#define sft(a,b,c)scanf("%d%d%d",&a,&b,&c)
#define for1(i,a,b) for(int i=(a);i=a;i--)
#define MEM1(a) memset(a,0,sizeof(a))
#define MEM2(a) memset(a,-1,sizeof(a))
#define LL __int64
const double PI = acos(-1.0);
template T gcd(T a, T b) {return b ? gcd(b, a % b) : a;}
template T lcm(T a, T b) {return a / gcd(a, b) * b;}
template inline T Min(T a, T b) {return a < b ? a : b;}
template inline T Max(T a, T b) {return a > b ? a : b;}
using namespace std;
int n,m;
#define N 1010
int a[N];
bool enem[N][N];
void Init(){
    memset(enem,false,sizeof(enem));
    for(int i = 0; i < N; i++)
        a[i] = i;
}
int geth(int t){
    if(a[t] == t){
        return t;
    }
    else{
        return a[t] = geth(a[t]);
    }
}
 
void merge(int u,int v){
    int t1 ,t2;
    t1 = geth(u);
    t2 = geth(v);
    if(t1 > t2){
        t1 ^= t2;
        t2 ^= t1;
        t1 ^= t2;
    }
    if(t1 != t2){
        a[t2] = t1;
    }
}
int main(){
    int p,x,y;
    while(sfd(n,m) != EOF){
        Init();
        for(int i = 0; i < m; i++){
            sft(p,x,y);
            if(p)
                merge(x,y);
            if(!p){
                enem[x][y] = true;            	//标记敌人
                enem[y][x] = true;		//一开始错就错在没加这句,既然是互为敌人那么两个都要标记!
            }
        }
        int temp = 0;
        for(int i = 1; i <= n; i++){
            for(int j = 1 ,indx = 0; j <= n; j++){
                if(enem[i][j] && indx == 0){
                    temp = j;			//第一个敌人和所有敌人都合并
                    indx++;
                }
                else if(enem[i][j]){
                    merge(temp,j);
                }
            }
        }
        int num = 0;
        for(int i = 1; i <= n; i++){
            if(a[i] == i)
                num++;
        }
        pf(num);
    }
    return 0;
}

这个算法还算比较简单。。



不过  简单是简单  当然有值得注意的地方

比如图中判断是否直接或者间接连接的问题时,例如CF的这道题

B. Mr. Kitayuta's Colorful Graph
time limit per test
1 second
memory limit per test
256 megabytes
input
standard input
output
standard output

Mr. Kitayuta has just bought an undirected graph consisting of n vertices and m edges. The vertices of the graph are numbered from 1 ton. Each edge, namely edge i, has a color ci, connecting vertex ai and bi.

Mr. Kitayuta wants you to process the following q queries.

In the i-th query, he gives you two integers — ui and vi.

Find the number of the colors that satisfy the following condition: the edges of that color connect vertex ui and vertex vi directly or indirectly.

Input

The first line of the input contains space-separated two integers — n and m (2 ≤ n ≤ 100, 1 ≤ m ≤ 100), denoting the number of the vertices and the number of the edges, respectively.

The next m lines contain space-separated three integers — aibi (1 ≤ ai < bi ≤ n) and ci (1 ≤ ci ≤ m). Note that there can be multiple edges between two vertices. However, there are no multiple edges of the same color between two vertices, that is, if i ≠ j,(ai, bi, ci) ≠ (aj, bj, cj).

The next line contains a integer — q (1 ≤ q ≤ 100), denoting the number of the queries.

Then follows q lines, containing space-separated two integers — ui and vi (1 ≤ ui, vi ≤ n). It is guaranteed that ui ≠ vi.

Output

For each query, print the answer in a separate line.

Sample test(s)
input
4 5
1 2 1
1 2 2
2 3 1
2 3 3
2 4 3
3
1 2
3 4
1 4
output
2
1
0
input
5 7
1 5 1
2 5 1
3 5 1
4 5 1
1 2 2
2 3 2
3 4 2
5
1 5
5 1
2 5
1 5
1 4
output
1
1
1
1
2
Note

Let's consider the first sample.

并查集的几道题目_第1张图片The figure above shows the first sample.
  • Vertex 1 and vertex 2 are connected by color 1 and 2.
  • Vertex 3 and vertex 4 are connected by color 3.
  • Vertex 1 and vertex 4 are not connected by any single color.


再例如这组数据:

5 4
2 3 1
2 5 1
2 4 1
2 1 1
4
1 2
1 3
1 4
1 5

通过合并先后把 (2,3) (2,5) (2,4) (2,1)  终点都是1这个点(通过颜色1连接)但是   3,4,5这三个点的终点并不是1,而是2,这就是这道题一直错的原因

说多了都是泪  贴代码!

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//#define ONLINE_JUDGE
#define eps 1e-8
#define INF 0x7fffffff											//INT_MAX
#define inf 0x3f3f3f3f    										//int范围内可靠的无穷大
#define FOR(i,a) for((i)=0;i<(a);(i)++)							//
#define MEM(a) (memset((a),0,sizeof(a)))
#define sfs(a) scanf("%s",a)
#define sf(a) scanf("%d",&a)
#define sfI(a) scanf("%I64d",&a)
#define pf(a) printf("%d\n",a)
#define pfI(a) printf("%I64d\n",a)
#define pfs(a) printf("%s\n",a)
#define sfd(a,b) scanf("%d%d",&a,&b)
#define sft(a,b,c)scanf("%d%d%d",&a,&b,&c)
#define for1(i,a,b) for(int i=(a);i=a;i--)
#define MEM1(a) memset(a,0,sizeof(a))
#define MEM2(a) memset(a,-1,sizeof(a))
#define LL __int64
const double PI=acos(-1.0);
template T gcd(T a,T b){return b?gcd(b,a%b):a;}
template T lcm(T a,T b){return a/gcd(a,b)*b;}
template inline T Min(T a,T b){return a inline T Max(T a,T b){return a>b?a:b;}
using namespace std;
int n,m,q;
#define N 110
int pic[N][N];
int num = 0;
int get_head(int p,int c){
	return pic[p][c] == p ? p : pic[p][c] = get_head(pic[p][c],c);
}
void merge(int p1,int p2,int c){
	int h1 = get_head(p1,c);
	int h2 = get_head(p2,c);
	if(h1 != h2){
		pic[h1 < h2 ? pic[p2][c] : pic[p1][c]][c] = h1 < h2 ? h1 : h2;
	}
}
void Init(){
	int point_1,point_2,color;
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= m; j++){
			pic[i][j] = i;
		}
	}
	for(int i = 0; i < m; i++){
		sft(point_1,point_2,color);
		merge(point_1,point_2,color);
	}
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= m; j++){
			pic[i][j] = get_head(pic[i][j],j);
		}
	}
}
void slove(){
	sf(q);
	int x,y;
	while(q--){
		num = 0;
		sfd(x,y);
		for(int i = 1; i <= m; i++){
			if(pic[x][i] == pic[y][i])
				num++;
		}
		pf(num);
	}
}
int main(){
#ifndef ONLINE_JUDGE
	freopen("in.txt","r",stdin);
//	freopen("Out.txt","w",stdout);
#endif
	while(~sfd(n,m)){
		Init();
		slove();
	}
	return 0;
}

你可能感兴趣的:(解题报告)