PAT----图----并查集

并查集, 连通分量

1107

// 1.如果说“error: ‘scanf’ was not declared in this scope”:
//       具体的解决: include "stdio.h"
//       统一解决: include <bits/stdc++.h>
// 2.并查集:
//     (1)father数组: 大小--节点的个数。  初始化--每个节点是自己的父节点。
//     (2)combine数组: 查看两个参数的父集是否相同, 不同则赋值。
//     (3)find: 如果父集不是自己就一直循环。 路径压缩
//     (4)解决的问题:判断连通分支个数

// 3.scanf是可以忽略" "的
// 4.注意把==和=写错的错误








//核心: 并查集----反向建树 + 连通分支个数 + 每个连通分支节点数 

//1.并查集的反向建树:  
//  (1)即输入数据不是每一波, 在一个联通分支下, 而是有相同叶子节点的在一个联通分支下 
//  (2)解决方法: 
//      方法一:
//          if(indexs[index]==0)indexs[index]=i;
//          combine(indexs[index], i);
//      方法二: 
//          利用一个二维向量vector<vector<int> >indexs, 反向建树即可 

//2.并查集寻找根节点:  
//  (1)准备roots[节点数]----就像数组型树一样:
//      1)并查集的题目, 很多数据给出的节点都是连续的, 并且数量是已知的:   vector<int>roots(节点数)
//      2)不连续,总个数位置:    myHash:  用大vector  或者  map, 但是map的缺点就是, 不能排序 

//3.并查集获得每个联通分支的个数:  在寻找根节点的时候即可找到  





// #include "stdio.h"
#include <bits/stdc++.h>
#include <vector>
#include <algorithm>

using namespace std;

vector<int>isRoot;
vector<int>father;
vector<int>hobby(1001, 0);



int find(int x) {
	
	int son, tmp;

	son = father[x];

	while (father[x] != x)x = father[x];
	
	while (son != x) {
		tmp = father[son];
		father[son] = x;
		son = tmp;
	}

	return x;
}

bool cmp(int a, int b) {
	return a > b;
}

void combine(int a, int b) {
	
	int fa1 = find(a);
	int fa2 = find(b);

	if (fa1 != fa2)father[fa2] = fa1;
}

int main() {
	
	//输入 和 初始化
	int n;
	scanf("%d", &n);
	isRoot.resize(n + 1);
	father.resize(n + 1);
	fill(isRoot.begin(), isRoot.end(), 0);

	for (int i = 1; i <= n; i++) {
		father[i] = i;
	}


	for (int i = 1; i <= n; i++) {
		int k;
		scanf("%d:", &k);
		//printf("K: %d ", k);
		for (int j = 0; j < k; j++) {
			int h;
			scanf("%d", &h);
			if (hobby[h] == 0)hobby[h] = i;
			combine(hobby[h],i);
		}
	}



	//
	for (int i = 1; i <= n; i++) {
		isRoot[find(i)]++;
	}

	int cnt=0;
	for (int i = 1; i <= n; i++) {
		if (isRoot[i] != 0)cnt++;
	}

	sort(isRoot.begin(), isRoot.end(), cmp);

	printf("%d\n", cnt);

	for (int i = 0; i < cnt; i++) {
		printf("%d", isRoot[i]);
		if (i != (cnt - 1))printf(" ");
		else printf("\n");
	}

    return 0;

}

并查集, 联通分量, 节点数据统计

1114 

//1.iterator的用法: 不能有两个一样名字的
//2.scanf没写“&”在编译时无法检查出
//3.scanf和printf的数据类型必须是对的, 否则会出错, 并且数字也很奇怪
//4.scanf和输入的数据类型没对上也会出错(比如是整数, 用%f接收不行)
//5.并查集的father需要直接用下标来表示关系, 所以通常是一个大的数组
//6.map迭代器访问元素用first,second
//7.两个整数相除想得到浮点数,不仅要等式左边为浮点数, 某一个数也要*1.0变成浮点数
//8.vector, set, map用的时候要想清楚了: 各自缺点:1.无法按值取  2.3.无法下标访问, 遍历只能迭代器
//9.某个对象所属变量太多的话, 使用struct, 用好初始化函数name():a(1),b(0){}
//10.并查集在累积数据时, 直接使用父集,而不是父节点。并且这个操作在combine函数(父节点在上次合并的时候算访问过)
//11.学会sort。cmp函数的使用
//12.并查集comnine函数认谁做一直的父节点也是一个点
//13.printf输出的格式要求在%之后, 并且, %0xd, %.xf.

#include <stdio.h>
#include <vector>
#include <set>
#include <map>
#include <algorithm>


using namespace std;

struct person {
	int id, num, ma, fa, set, area;
	double setAvg, areaAvg;
	vector<int>childs;
	person() :num(1), set(0), area(0),ma(-1),fa(-1) {}

};

vector<int>father(10001);
map<int, person>persons;
vector<person>result;
//set<int>ids;

bool cmp(person a, person b) {
	if (a.areaAvg != b.areaAvg)
		return a.areaAvg > b.areaAvg;
	else return a.id < b.id;
}

int find(int x) {

	int son = father[x];
	while (father[x] != x)x = father[x];

	while (son != x) {
		int tmp = father[son];
		father[son] = x;
		son = tmp;
	}

	return x;
}


void combine(int a, int b) {

	int fa1 = find(a);
	int fa2 = find(b);

	if (fa1 < fa2) {
		father[fa2] = fa1;
		persons[fa1].num += persons[fa2].num;  //?
		persons[fa1].set += persons[fa2].set;
		persons[fa1].area += persons[fa2].area;
	}
	else if (fa1 > fa2) {
		father[fa1] = fa2;
		persons[fa2].num += persons[fa1].num;  //?
		persons[fa2].set += persons[fa1].set;
		persons[fa2].area += persons[fa1].area;
	}

}

int main() {
	int n;
	scanf("%d", &n);

	for (int i = 0; i < father.size(); i++) father[i] = i;

	for (int i = 0; i < n; i++) {
		person tmp;
		int k, ch;
		scanf("%d %d %d %d", &tmp.id, &tmp.ma, &tmp.fa, &k);

		//ids.insert(tmp.id);


		for (int j = 0; j < k; j++) {
			scanf("%d", &ch);
			tmp.childs.push_back(ch);
			//combine(id, ch);

		}

		scanf("%d %d", &tmp.set, &tmp.area);
		persons[tmp.id] = tmp;
	}

	检查输入
	//map<int, person>::iterator it1;
	//for (it1 = persons.begin(); it1 != persons.end(); it1++) {
	//	printf("id: %d ma: %d fa: %d  chsize: %d  se: %d ar: %d\n", it1->second.id, it1->second.ma, it1->second.fa, it1->second.childs.size(), it1->second.set, it1->second.area);
	//}

	//并
	map<int, person>::iterator it2;
	for (it2 = persons.begin(); it2 != persons.end(); it2++) {

		if (it2->second.ma != -1) {
			combine(it2->second.id, it2->second.ma);
		}

		if (it2->second.fa != -1) {
			combine(it2->second.id, it2->second.fa);
		}

		for (int j = 0; j < it2->second.childs.size(); j++) {
			combine(it2->second.id, it2->second.childs[j]);
		}
	}

	检查person
	//map<int, person>::iterator it3;
	//printf("检查person: \n");
	//for (it3 = persons.begin(); it3 != persons.end(); it3++) {
	//	printf("id: %d ma: %d fa: %d  chsize: %d  se: %d ar: %d\n", it3->second.id, it3->second.ma, it3->second.fa, it3->second.childs.size(), it3->second.set, it3->second.area);
	//}

	检查father
	//printf("FATHER: \n");
	//for (int i = 0; i < father.size(); i++) {
	//	if (persons.find(i) != persons.end() && (father[i] == i)) {

	//		printf("%d:%d ", i, father[i]);
	//		if (i % 10 == 0)printf("\n");
	//	}

	//}

	//结果
	for (int i = 0; i < father.size(); i++) {
		if (persons.find(i) != persons.end() && (father[i] == i)) {
			person tmp;
			tmp.id = i;
			tmp.num = persons[i].num;
			//printf("set: %d\n", persons[i].set);
			//printf("area: %d\n", persons[i].area);
			//printf("num: %d\n", persons[i].num);
			tmp.setAvg = persons[i].set*1.0 / persons[i].num;
			tmp.areaAvg = persons[i].area*1.0 / persons[i].num;
			result.push_back(tmp);
		}
	}



	//排序
	sort(result.begin(), result.end(), cmp);

	//输出
	printf("%d\n", result.size());
	for (int i = 0; i < result.size(); i++) {
		printf("%04d %d %.3f %.3f\n", result[i].id, result[i].num, result[i].setAvg, result[i].areaAvg);
	}


	return 0;
}

 自己写了一版又臭又长的代码



//看到题目中有: 4-digit的时候, 小心输出


//并查集如果有父亲或者孩子, 但没有属性, 记得空的节点也是要插入的
//涉及double的题目, 要小心变量声明, 输入, 输出
//map插入节点的时候要小心, 尤其是这种 父亲, 孩子, 一大堆的 


#include<iostream>
#include<vector>
#include<unordered_map>
#include<algorithm>

using namespace std;

struct node {
	int id, root;
	double set, area;

	node() {
		root = 0;
		area = 0;
		set = 0;
	}

	node(int x) {
		id = x;
		root = 0;
		area = 0;
		set = 0;
	}

	node(int x, double y, double z) {
		id = x;
		set = y;
		area = z;
		root = 0;
	}
};

int n;

vector<int>father(10010);
unordered_map<int, node>inputs;
vector<int>roots;
vector<node>results;

int find(int x) {
	int son = father[x];
	while (father[x] != x)x = father[x];
	while (son != x) {
		int tmp = father[son];
		father[son] = x;
		son = tmp;
	}
	return x;
}

void combine(int a, int b) {
	int fa1 = find(a);
	int fa2 = find(b);

	if (fa1 < fa2)father[fa2] = fa1;
	else father[fa1] = fa2;
}

//area降序
//id升序
bool cmp(node a, node b) {
	if (a.area != b.area)return a.area > b.area;
	else return a.id < b.id;
}

int main() {
	scanf("%d", &n);

	for (int i = 0; i < father.size(); i++)father[i] = i;

	for (int i = 0; i < n; i++) {
		int tmpId, tmpFa, tmpMa, tmpNum;
		double tmpSet, tmpArea;
		scanf("%d %d %d %d", &tmpId, &tmpFa, &tmpMa, &tmpNum);


		if (tmpFa != -1) {
			node tmpNodeFa(tmpFa);
			combine(tmpId, tmpFa);
			if(inputs.find(tmpFa)==inputs.end()||(inputs[tmpFa].area==0))inputs[tmpFa] = tmpNodeFa;
		}
		if (tmpMa != -1) {
			node tmpNodeMa(tmpMa);
			combine(tmpId, tmpMa);
			if(inputs.find(tmpMa)==inputs.end() || (inputs[tmpMa].area == 0))inputs[tmpMa] = tmpNodeMa;
		}

		for (int j = 0; j < tmpNum; j++) {
			int tmp;
			scanf("%d", &tmp);
			node tmpNodeCh(tmp);
			if(inputs.find(tmp)==inputs.end() || (inputs[tmp].area == 0))inputs[tmp] = tmpNodeCh;
			combine(tmpId, tmp);
		}

		scanf("%lf %lf", &tmpSet, &tmpArea);
		//cout <<"%%%%%%%%%%%" <<tmpSet <<"   "<< tmpArea << endl;
		node tmpNode(tmpId, tmpSet, tmpArea);
		if(inputs.find(tmpId)==inputs.end() || (inputs[tmpId].area == 0))inputs[tmpId] = tmpNode;
		//cout << "%%%%" << inputs[tmpId].id << " " << inputs[tmpId].set<<"^^^"<<inputs[tmpId].area << endl;

	}



	检查输入
	//unordered_map<int, node>::iterator it2;
	//for (it2 = inputs.begin(); it2 != inputs.end(); it2++) {
	//	cout << it2->first << ":$$$ " << it2->second.set << ": " << it2->second.area << ": " << it2->second.root << ": " << endl;
	//}



	unordered_map<int, node>::iterator it;
	for (it = inputs.begin(); it != inputs.end(); it++) {
		//为自己的根节点计数 
		inputs[find(it->first)].root++;
		//将需要的加和入根节点
		if (inputs[find(it->first)].id != it->first) {
			inputs[find(it->first)].set += it->second.set;
			inputs[find(it->first)].area += it->second.area;
		}
	}


	检查输入
	//cout << "################################" << endl;
	//unordered_map<int, node>::iterator it3;
	//for (it3 = inputs.begin(); it3 != inputs.end(); it3++) {
	//	cout << it3->first << ": " << it3->second.set << ": " << it3->second.area << ": " << it3->second.root << ": " << endl;
	//}

	//cout << "################################" << endl;
	unordered_map<int, node>::iterator it1;
	for (it1 = inputs.begin(); it1 != inputs.end(); it1++)
		if (it1->second.root != 0) {
			//cout << it1->first << ": " << it1->second.set << ": " << it1->second.area << ": " << it1->second.root << "####" << endl;
			it1->second.area /= it1->second.root;
			it1->second.set /= it1->second.root;
			results.push_back(it1->second);
		}
	sort(results.begin(), results.end(), cmp);

	//cout << "################################" << endl;
	//输出的时候记得%04d
	printf("%d\n", results.size());
	for (int i = 0; i < results.size(); i++) {
		printf("%04d %d %.3lf %.3lf\n", results[i].id, results[i].root, results[i].set, results[i].area);

	}

	return 0;
}

并查集+ 联通分量个数+ 节点个数+ 判断两个节点是否在同一个子集

1118

//1.并查集比较绕的点是找谁union, 如果没有要求的话, 找第一个输入的就可以
//2.并查集combine的时候,最好将其他节点作为first的子集
//3.并查集father的初始化是默认的一步
//4.并查集如何记录有多少个节点:输入节点可以用set或map记录
//5.并查集如何判断有几个子集:看father[i]==i的个数
//6.并查集如何判断两个点是否在同一个分支中:find(a)==find(b)
//7.并查集的组成部分: 
    // (1)初始化
    // (2)确定first
    // (3)combine
    // (4)find





//并查集:
//    1.数据结构:  
//        (1)vector<int>father;   //一开始就 father[i]=i; 初始化 
//
//    2.函数:
//        (1)int find(int x){   
// 
//            int son=father[x];
//            while(father[x]!=x)x=father[x];
//
//            while(son!=x){               //路径压缩
//                int tmp=father[son];
//                father[son]=x;
//                son=tmp;
//             }
//
//            return x;   //找到父节点很容易
//            }
//
//         (2)void combine(int a, int b){
//                int fa1=find(a);
//                int fa2=find(b);
//    
//                if(fa1!=fa2)father[fa2]=fa1;
//
//            }
//
//    3.操作:
//        (1)初始化:  father[i]=i 
//        (2)插入节点:  combine(a,b);      //可以找一个节点作为参照 
//        (3)连通分支个数:  
//                方法一:for(father)if(father[i]==i&&(sets.find()!=sets.end())) 
//                方法二:
//                        vector<int>myHash;
//                        for(所有节点)myHash[find(i)]++;
//                        if(myHash[i]!=0)roots.push_back();
//         (4)判断是否同一个连通分支: find(a)==find(b)



#include <stdio.h>
#include <vector>
#include <set>


using namespace std;

vector<int>father(10001);
set<int>birds;
vector<int>results;

int find(int x) {
	
	int son = father[x];
	while (father[x] != x)x = father[x];

	while (son != x) {
		int tmp = father[son];
		father[son] = x;
		son = tmp;
	}
	return x;
}

void combine(int a, int b) {
	int fa = find(a);
	int fb = find(b);

	if (fa != fb) father[fb] = fa;
}

int main() {

	int n;
	scanf("%d", &n);   //一进来输入n是常态了 

	for (int i = 0; i < father.size(); i++)father[i] = i;  //并查集的father先初始化也是常态

	for (int i = 0; i < n; i++) {
		int k, first, bird;
		scanf("%d %d", &k, &first);  //写错一个逗号, 检查半天
		birds.insert(first);

		for (int j = 1; j < k;j++) {
			scanf("%d", &bird);
			birds.insert(bird);
			combine(first, bird);
		}
	}


	for (int i = 0; i < father.size(); i++) {
		if (father[i] == i&&(birds.find(i)!=birds.end()))results.push_back(i);
	}

	printf("%d %d\n", results.size(), birds.size());

	int k, a, b;
	scanf("%d", &k);
	for (int i = 0; i < k; i++) {
		scanf("%d %d", &a, &b);
		if (find(a) == find(b))printf("Yes\n");
		else printf("No\n");
	}

	return 0;
}










//测试时的代码:
//核心:  并查集:连通分支个数 + 并查集:父节点

#include<iostream>
#include<vector>
#include<set>

using namespace std;

int n, queries;
int birdNum, treeNum;
vector<int>father(10010);
set<int>birds;

int find(int x) {
	int son = father[x];

	while (father[x] != x)x = father[x];

	while(son != x) {
		int tmp = father[son];
		father[son] = x;
		son = tmp;
	}

	return x;
}

void combine(int a, int b) {  //?结合的方式不同会不会导致根节点不同   
	int fa1 = find(a);
	int fa2 = find(b);

	if (fa1 != fa2)father[fa2] = fa1;
}

//节点个数
//根个数

int main() {
	scanf("%d", &n);

	for (int i = 0; i < father.size(); i++)father[i] = i;

	for (int i = 0; i < n; i++) {
		int num, tmp1, tmp2, max = -1;
		scanf("%d", &num);

		scanf("%d", &tmp1);
		birds.insert(tmp1);
		for (int j = 1; j < num; j++) {
			scanf("%d", &tmp2);
			birds.insert(tmp2);

			combine(tmp1, tmp2);
		}
	}
    //鸟的个数
	birdNum = birds.size();
	vector<int>roots(birdNum+1,0);
	for (int i = 1; i <= birdNum; i++)
		roots[find(i)]++;

    //连通分支个数 
	for (int i = 1; i <= birdNum; i++)
		if (roots[i] != 0)treeNum++;

	printf("%d %d\n", treeNum, birdNum);

    //判断是否同一个连通分支 
	scanf("%d", &queries);
	for (int i = 0; i < queries; i++) {
		int a, b;
		scanf("%d %d", &a, &b);

		int faA = find(a);
		int faB = find(b);

		if (faA == faB)printf("Yes\n");
		else printf("No\n");
	}

	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值