题目描述:
假如已知有n个人和m对好友关系(存于数字r)。如果两个人是直接或间接的好友(好友的好友的好友...),则认为他们属于同一个朋友圈,请写程序求出这n个人里一共有多少个朋友圈。
假如:n = 5 , m = 3 , r = {{1 , 2} , {2 , 3} , {4 , 5}},表示有5个人,1和2是好友,2和3是好友,4和5是好友,则1、2、3属于一个朋友圈,4、5属于另一个朋友圈,结果为2个朋友圈。
输入:
输入包含多个测试用例,每个测试用例的第一行包含两个正整数 n、m,1=<n,m<=100000。接下来有m行,每行分别输入两个人的编号f,t(1=<f,t<=n),表示f和t是好友。 当n为0时,输入结束,该用例不被处理。
输出:
对应每个测试用例,输出在这n个人里一共有多少个朋友圈。
样例输入:
5 3
1 2
2 3
4 5
3 3
1 2
1 3
2 3
0
样例输出:
2
1
【解题思路】
这应该是并查集最简单的应用了,求集合个数。
基本思路:
1、每次输入节点的时候,就合并。
2、依次查找每个节点的父节点。
3、放在set中,set.size()即为朋友圈的个数。
并查集的应用很多,在很多时候效率比prim以及Dijkstra算法效率要高。
后面会有一些题两者都可以做出。我会给出比较。
C++ AC
#include <stdio.h>
const int maxn = 100002;
int parent[maxn];
int n;
int m;
int i;
int findParent(int f) {
while(parent[f] != f){
f = parent[f];
}
return f;
}
void unionTwo(int f, int t) {
int a = findParent(f);
int b = findParent(t);
if (a == b) return;
if (a > b) {
parent[a] = b;
} else {
parent[b] = a;
}
}
int main(){
while(scanf("%d",&n) != EOF){
if(n == 0){
break;
}
scanf("%d",&m);
if(n == 1){
printf("1\n");
}else{
for(i = 1; i < n+1; i++){
parent[i] = i;
}
for(i = 0 ; i < m ; i++){
int a, b;
scanf("%d%d",&a,&b);
unionTwo(a,b);
}
for (i = 1; i < n+1; i++) {
parent[i] = findParent(i);
}
int num = 0;
for(i = 1; i < n+1; i++){
if(parent[i] == i){
num ++;
}
}
printf("%d\n",num);
}
}
return 0;
}
/**************************************************************
Problem: 1526
User: wangzhenqing
Language: C++
Result: Accepted
Time:210 ms
Memory:1412 kb
****************************************************************/
Java AC
import java.io.StreamTokenizer;
import java.util.HashSet;
import java.util.Set;
public class Main {
/*
* 1526
*/
public static void main(String[] args) throws Exception{
StreamTokenizer st = new StreamTokenizer(System.in);
while (st.nextToken() != StreamTokenizer.TT_EOF) {
int n = (int) st.nval;
if (n == 0) {
break;
}
if (n == 1) {
System.out.println(1);
}else {
int []parent = new int[n+1];
for (int i = 1; i <= n; i++) {
parent[i] = i;
}
st.nextToken() ;
int m = (int) st.nval;
for (int i = 0; i < m; i++) {
st.nextToken() ;
int f = (int) st.nval;
st.nextToken() ;
int t = (int) st.nval;
union(f ,t , parent );
}
for (int i = 1; i < n+1; i++) {
parent[i] = findParent(i, parent);
}
Set<Integer> numSet = new HashSet<Integer>();
for (int i = 1; i < n+1; i++) {
numSet.add(parent[i]);
}
System.out.println( numSet.size());
}
}
}
private static void union(int f, int t, int[] parent ) {
int a = findParent(f , parent);
int b = findParent(t , parent);
if (a == b) return;
if (a > b) {
parent[a] = b;
} else {
parent[b] = a;
}
}
private static int findParent(int f, int[] parent) {
if (parent[f] == f) {
return f;
}
return findParent(parent[f],parent );
}
}
/**************************************************************
Problem: 1526
User: wzqwsrf
Language: Java
Result: Accepted
Time:970 ms
Memory:42400 kb
****************************************************************/
其实在查找父节点的时候,不应该用递归,
直接用while效率比较高。
这在一些代码中有体现。
将parent[]声明为公共属性。
将以下这个方法修改为:
修改前:
private static int findParent(int f, int[] parent) {
if (parent[f] == f) {
return f;
}
return findParent(parent[f],parent );
}
修改后:
private static int findParent(int f) {
while (parent[f] != f) {
f = parent[f];
}
return f;
}