PAT A1034:Head of a Gang(DFS和并查集)

参考《算法笔记》P354
考点:并查集、连通图、DFS
题目地址

方法一:DFS


#include<iostream>
#include<map>
#include<string>
using namespace std;

//最大人数 
const int maxn=2010;
//人与人之间的邻接矩阵 
int G[maxn][maxn],weight[maxn];
bool visit[maxn]={false};
//pNum表示总人数 
int pNum=0,n,k;

//名字到编号 
map<string,int> stringToInt;
//编号到名字
map<int ,string> intToString;
//头目名字到团队数量
map<string,int> Gang; 


//1. 访问单个连通块 
void DFS(int nowVisit,int &head,int &pMember,int &totalValues){
	//参数分别是现在访问的点,头目编号,头目的点权,这个连通图的总边权
	pMember++;
	visit[nowVisit]=true;
	if(weight[nowVisit]>weight[head]){
		head=nowVisit;
	}
	for(int i=0;i<pNum;i++){
		if(G[nowVisit][i]){
			totalValues+=G[nowVisit][i];
			G[nowVisit][i]=G[i][nowVisit]=0;
			if(visit[i]==false){
				DFS(i,head,pMember,totalValues);
			}
		}
	}
}




//2.遍历整个图的所有连通块 
void DFSTraversal(){
	for(int i=0;i<pNum;i++){
		if(visit[i]==false){
			int head=i,pMember=0,totalValues=0;
			DFS(i,head,pMember,totalValues);
			if(pMember>2&&totalValues>k){
				Gang[intToString[head]]=pMember;
			}
		}
	}
} 

//3.输入名字,返回编号 
int change(string str){
	//名字出现过,则返回之前给的编号
	if(stringToInt.find(str)!=stringToInt.end()){
		return stringToInt[str];
	} else{
		stringToInt[str]=pNum;
		intToString[pNum]=str;
		return pNum++;
	}
} 
int main(){
//	freopen("in.txt","r",stdin);
	int w;
	string str1,str2;
	cin>>n>>k;
	for(int i=0;i<n;i++){
		cin>>str1>>str2>>w;
		//将人名改为编号
		int id1=change(str1);
		int id2=change(str2);
		//改变相关的两人的点权 
//		printf("------%d %d------\n",id1,id2);
		weight[id1]+=w;
		weight[id2]+=w;
		//改变两人之间的边权 
		G[id1][id2]+=w;
		G[id2][id1]+=w; 
	}
	//邻接矩阵和点权数组信息输入完毕,开始深搜
	//从每一个没有访问的人的点开始->dfs,traversal(遍历)
	DFSTraversal();
	//获取Gang映射的所有头目 
	map<string,int>::iterator it;
	//1.输出团伙数量 
	cout<<Gang.size()<<endl;
	for(it=Gang.begin();it!=Gang.end();it++){
		//2.一行一行输出头目和团员数量 
		cout<<it->first<<" "<<it->second<<endl; 
	} 
	fclose(stdin);
	return 0;
} 

方法二:并查集


/*-----方法二--->>>-并查集 -*/
//
//最大人数 
const int maxn=2010;
//人与人之间的邻接矩阵 
//矩阵,每个人的点权,以这个下标为根节点(头目)的集合(团伙,set)的总边权,以这个下标为根节点的集合 的总人数 
int G[maxn][maxn],weight[maxn],setValues[maxn]={0},setMembers[maxn]={0};
bool visit[maxn]={false};
//pNum表示总人数 
int n,k,pNum;
int father[maxn]={0};

//名字到编号 
map<string,int> stringToInt;
//编号到名字
map<int ,string> intToString;
//头目名字到团队数量
map<string,int> Gang; 	
//3.输入名字,返回编号 
int change(string str){
	//名字出现过,则返回之前给的编号
	if(stringToInt.find(str)!=stringToInt.end()){
		return stringToInt[str];
	} else{
		stringToInt[str]=pNum;
		intToString[pNum]=str;
		return pNum++;
	}
} 
int findFather(int a){
	//如果a不是头目 
	if(a!=father[a]){
		//如果a的权值比根大,改变a为头目 
		if(weight[a]>weight[father[a]]){
			//原来的头目改跟a混 
			setValues[a]=setValues[father[a]];
			setValues[father[a]]=0;
			setMembers[a]=setMembers[father[a]];
			setMembers[father[a]]=0; 
			father[father[a]]=a;
			father[a]=a;

			return a;
		} else{//还是原来的头目 
			return father[a]=findFather(father[a]);
		} 
	//a是根,则返回根 
	}
	return a;
}
void init(){
	for(int i=0;i<maxn;i++){
		father[i]=i;
		setMembers[i]=1;	
	}
}
int Union(int a,int b){
	//a,b是加了边权的,可能会比自己的父亲权值大 
	int fa=findFather(a);
	int fb=findFather(b);
	//如果两个点不是一个集合则合并 
	//取两个小团伙的点权最大的头目 ,并删除小头目的总边权,给大头目 
	if(fa!=fb){
		if(weight[fa]>=weight[fb]){
			setValues[fa]+=setValues[fb];
			setValues[fb]=0;
			setMembers[fa]+=setMembers[fb];
			setMembers[fb]=0;
			father[fb]=fa;
			return fa;
		} else{
			setValues[fb]+=setValues[fa];
			setValues[fa]=0;
			setMembers[fb]+=setMembers[fa];
			setMembers[fa]=0;
			father[fa]=fb;
			return fb;
		}
	}
	else {
		return fa;
	}
}
int main(){
	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	int w;
	string str1,str2;
	cin>>n>>k;
	int father[maxn]={0};	
	init();
	for(int i=0;i<n;i++){
		cin>>str1>>str2>>w;
		//将人名改为编号
		int id1=change(str1);
		int id2=change(str2);
		//改变相关的两人的点权 
		weight[id1]+=w;
		weight[id2]+=w;
		//合并两点,并返回合并之后的头目
		int head=Union(id1,id2);
		setValues[head]+=w; 
	}
	//筛选符合条件的团伙数量 
	for(int i=0;i<pNum;i++){
		if(setValues[i]>k&&setMembers[i]>2){
			Gang[intToString[i]]=setMembers[i]; 
		}
	}
	cout<<Gang.size()<<endl;
	map<string,int>::iterator it1;
	for(it1=Gang.begin();it1!=Gang.end();it1++){
		//一行一行输出头目和团员数量 
		cout<<it1->first<<" "<<it1->second<<endl; 
	}
//	fclose(stdin);
	return 0;
} 

看了一博主的并查集方法写的蛮好

传送门

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值