并查集解决团队划分问题(连通集)
总结:主要思路还是并查集,并查集的操作模板化,只用修改结构体即可
个人结点信息较多,故用结构体;
团伙由于包含人数、头目名字,且要按照字典序输出,故也用结构体
- 个人结点:名字、通话时长
- 团伙结点:人数、头目名字
map<string,int>Getindex: 名字→个人结点的下标,且可以判断是否为第一次出现
输入:
新人员→生成新节点
获取输入的2个人的结点下标
更新通话时长,合并
遍历:根据顶级联系人划分团伙(为减少开销,设置访问标记)
判断团伙条件
输出:
#include<iostream>
#include<string>
#include<map>
#include<algorithm>
using namespace std;
map<string,int>Getindex;//结点名字→数组下标(既是并查集下标,又是结构体下标)
const int maxn=2001;
int father[maxn];//并查集
int height[maxn];//高度
bool flag[maxn];//遍历标记
void Initial(int n){//初始化
for(int i=0;i<=2*n;i++){//最多映射到1~2*n
father[i]=i;
height[i]=1;
flag[i]=false;
}
}
int Find(int x){
if(x!=father[x]){
father[x]=Find(father[x]);
}
return father[x];
}
void Union(int x,int y){
int a=Find(x);
int b=Find(y);
if(a==b)return;
else if(height[a]>height[b])father[b]=a;
else if(height[a]<height[b])father[a]=b;
else{
father[b]=a;
height[a]++;
}
}
struct person{//个人结点
string name;//名字
int calltime;//通话总时长
person(string a,int t):name(a),calltime(t){}
person(){}
};
person badguy[maxn];
struct party{//团伙结点(用于输出)
string headname;//头目名字
int people;//人数
bool operator <(party x)const{//为了按字典序排序
return headname<x.headname;
}
};
int main(){
int n,k;
while(scanf("%d%d",&n,&k)!=EOF){
int i=1;//映射值从1开始
Getindex.clear();
Initial(n);
while(n--){//输入
string s1,s2;
int t;
cin>>s1>>s2>>t;
if(!Getindex[s1]){//映射为0即表示第一次出现
Getindex[s1]=i;
height[i]=1;
badguy[i++]=person(s1,0);//生成个人结点
}
if(!Getindex[s2]){
Getindex[s2]=i;
height[i]=1;
badguy[i++]=person(s2,0);
}
int index1=Getindex[s1];
int index2=Getindex[s2];
badguy[index1].calltime+=t;//累计个人通话时长
badguy[index2].calltime+=t;
Union(index1,index2);//合并同伙
}
party ans[i-1];//团伙结点数组
int sum=0;
int p=0;
for(int j=1;j<i;j++){//外层:以badguy[j]初始化团伙结点信息
if(flag[j])continue;
flag[j]=true;
int sumtime=badguy[j].calltime;
int people=1;
int maxindex=j;//临时头目下标(通话时长最长)
for(int r=j+1;r<i;r++){//内层:找badguy[j]的同伙
if(flag[r])continue;
if(Find(r)==Find(j)){//相同顶级联系人
//cout<<"member"<<badguy[r].name<<endl;
if(badguy[r].calltime>badguy[maxindex].calltime)maxindex=r;//更新临时头目下标
sumtime+=badguy[r].calltime;
people++;
flag[r]=true;
}
}
if(sumtime>2*k&&people>2){//判断团伙条件
sum++;
ans[p].headname=badguy[maxindex].name;
ans[p++].people=people;
}
}
if(sum==0)printf("0\n");//输出
else{
printf("%d\n",sum);
sort(ans,ans+p);//字典序
for(int m=0;m<p;m++){
cout<<ans[m].headname<<" "<<ans[m].people<<endl;
}
}
}
return 0;
}