参考《算法笔记》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;
}
看了一博主的并查集方法写的蛮好