PAT 1034 Head of a Gang C++版

PAT 1034 Head of a Gang C++版

1.题意

给出一串用户通话的记录(记录形式是[name1,name2,time])。互相之间有通话关系的算是在同一个团队中。现在让你求出有多少个团伙,并且求出这个团伙中通话最多的人,以及输出这个团伙的总人数。

2.分析

解决这个问题可以使用两种方法,两种方法都较简单。一种是深搜法,一种是并查集法。并查集对于解决这种问题十分适合。所以笔者强烈建议对于这种题一律使用并查集解决。 【但是深搜的方法一定要会,因为别的题目会使用到】

  • 并查集
    step1 :将通话的name做一个到id的映射。
    step2:将有通话记录的人员归为相同伙【归并原则可以设置成:往下标小的方向合并,即设置小id的作为父id。】,可以用 unionTwo()方法实现。
    step3:输出正确结果
  • 深搜

3.代码

#include
#include
#include
#include
#include
#include
#include

#define maxn 10005
using namespace std;

struct People{	
	string name;
	int callTime;//通话时长 	
	int totalNum  = 0;//该组织下的总人数 
	int totalRel = 0;//该组织的总权重 
	int maxCallId;//该组中通话时长最长人的id 
}; 

struct Result{	
	string name;//姓名 
	int num;//数目 
}; 

map<string,int> sstoi;//字符串对应int 
map<int,string> iitos;//int对应字符串 
People peo[maxn]; 
int N,K; 
int father[maxn];//表示各个对象的父节点 

//排序操作 
int cmp(Result p1,Result p2){
	return p1.name < p2.name;
}

//字符串转数字
int PEO_NUM = 0; 
void convert(string name){
	if(sstoi.find(name) == sstoi.end()){//如果未找到这个字符串 
		sstoi[name] = PEO_NUM;
		iitos[PEO_NUM] = name;
		PEO_NUM ++; //自增处理一下
	}
}

//初始化操作 
void init(){	
	for(int i = 0; i< maxn;i++){
		father[i] = i;//将每个人设置成自己的 father 
	} 
	for(int i = 0;i < PEO_NUM ;i++){// 只有PEO_NUM 个人 
		peo[i].maxCallId = i;//自己的最大值的id = 自己 
	}
} 

//查找父对象
//这里的父对象,按照通话时长大的判断 
int findFather(int a){
	while(father[a] != a){//如果父节点不是自己 
		a =	father[a];//更新父节点 
	}
	return a;//返回a 
} 

//合并两个节点 
void unionTwo(int a,int b){
	int fa = findFather(a);
	int fb = findFather(b);
	if(fa < fb){//更新父节点 
		father[fb] = fa;
	}
	else{
		father[fa] = fb;
	}
} 

int main(){	
	cin >> N >> K;
	string call1,call2;//通话的两个人
	int time;//通话时间 
	int i;		
	init();
	 
	for (i = 0;i < N;i++ ){
		cin >> call1 >> call2 >> time; 		
		convert(call1);
		convert(call2);//先把两个人的姓名写入到map中
		peo[sstoi[call1]].name = call1; 
		peo[sstoi[call1]].callTime += time;//通话时间累加		 		
		
		peo[sstoi[call2]].name = call2; 
		peo[sstoi[call2]].callTime += time;//通话时间累加	
		
		//将两个用户合并成一个家族 
		//合并的规则:取较小的id作为父id  
		unionTwo(sstoi[call1],sstoi[call2]);
	}
		 
	set<int> gangNum ;//gang number 
	
	//开始处理这些 gang 
	for(i = 0;i< PEO_NUM;i++){	
		int maxTime = 0;
		int fa = findFather(i);
		peo[fa].totalNum += 1;//更新总人数 
		peo[fa].totalRel += peo[i].callTime;// 更新总权重 
		gangNum.insert(findFather(i));//查找节点i的father,并放到set中 		
		
		//找出最长时间通话的人 
		if( peo[peo[fa].maxCallId].callTime < peo[i].callTime){
			peo[fa].maxCallId = i;//更新i的值 
		}
	}
	
	//删除一些不符合条件的数据
	//人数 <= 2; 总联系权重大于K 	
	for(set<int> :: iterator it = gangNum.begin(); it!=gangNum.end();it++){		
		if(peo[*it].totalNum <= 2 || (peo[*it].totalRel / 2) <= K ){
			gangNum.erase(*it);		
		}		
	}
	
	cout <<gangNum.size()<<"\n"; 
	
	Result res[maxn];//保存结果  用于排序 
	int index = 0;
	for(set<int> :: iterator it = gangNum.begin(); it!=gangNum.end();it++){	
		res[index].name = peo[peo[*it].maxCallId].name;//赋值操作 
		res[index].num = peo[*it].totalNum;//赋值操作 		
		index++;
	}
	
	sort(res,res+index,cmp) ;
	for(i = 0;i< index;i++ ){
		cout << res[i].name <<" "<< res[i].num<<"\n"	;
	}
} 

4.测试用例

8 59
DDD EEE 5
EEE DDD 70
FFF GGG 30
GGG HHH 20
HHH FFF 10
AAA BBB 10
BBB AAA 20
AAA CCC 40

5.注意点

5.1 如何得到团伙数?

得到这个数字很简单。主要有一下两种方法:

  • father[i] = i 的即是一个 gang 。判断一共有多少个i满足 father[i]=i即可
  • fa添加到set中,然后判断set的size。
5.2 段错误

在提交的过程中,遇到了一个段错误,如下所示:
PAT 1034 Head of a Gang C++版_第1张图片
错误的原因是:我一直以为是人数 是 <=1000的。但是后来才知道,人数不一定满足这个关系。【Each input file contains one test case. For each case, the first line contains two positive numbers N and K (both less than or equal to 1000), the number of phone calls and the weight threthold, respectively.N代表的是输出的行数,而不是人数。这样的话,人数maxn 就至多会是2000。修改一下maxn的定义即可。

你可能感兴趣的:(#,PAT)