【PAT甲级】1034 Head of a Gang (30分):图的遍历DFS

1034 Head of a Gang (30分)

One way that the police finds the head of a gang is to check people's phone calls. If there is a phone call between A and B, we say that A and B is related. The weight of a relation is defined to be the total time length of all the phone calls made between the two persons. A "Gang" is a cluster of more than 2 persons who are related to each other with total relation weight being greater than a given threshold K. In each gang, the one with maximum total weight is the head. Now given a list of phone calls, you are supposed to find the gangs and the heads.

Input Specification:

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. Then N lines follow, each in the following format:

Name1 Name2 Time

      
    

where Name1 and Name2 are the names of people at the two ends of the call, and Time is the length of the call. A name is a string of three capital letters chosen from A-Z. A time length is a positive integer which is no more than 1000 minutes.

Output Specification:

For each test case, first print in a line the total number of gangs. Then for each gang, print in a line the name of the head and the total number of the members. It is guaranteed that the head is unique for each gang. The output must be sorted according to the alphabetical order of the names of the heads.

Sample Input 1:

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

      
    

Sample Output 1:

2
AAA 3
GGG 3

      
    

Sample Input 2:

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

      
    

Sample Output 2:

0

分析:图的遍历DFS

  • 无向图(边权需要插两次)

顶点:人

边:A和B有通话记录

边权:A和B之间的总通话时长

点权:A参与的通话时长之和

顶点大于2个且相互之间连通,总边权大于给定的阈值K的连通块为 犯罪团体;总点权最大的顶点是 老大。

输出 犯罪团体的个数、每个团体的老大的名字和团体人数,名字按字典序排序。


p.s:在无向图中,如果两个顶点之间可以相互到达(可以是通过一定路径间接到达),那么就称这两个顶点连通。

  • 最大顶点个数应该要>2000 !!!

    这个一开始没注意,结果一直报错

    因为题目要求:通话记录N<=1000,

    若每通电话都是不同两人打的就有2000人了。

  • 老大名字输出按照字典序,用map<string,int>(老大名字 -> 团体人数)存,自动排序。


代码

#include<iostream>
#include<string>
#include<map>
#include<queue>

using namespace std;

const int maxN=2010;//总人数 
const int INF=1000000000;//表示无效边
int G[maxN][maxN]; //邻接矩阵实现无向图
map<string ,int> nameMap;//人名与编号
map<int ,string> nameMap2;//编号与人名
map<int,int> vWeight;//点权
bool vis[maxN]; //访问数组
int N,K;//通话记录的数量,边权的阈值
map<string,int> gangMap;//老大的名字 -> 犯罪团体人数

/*遍历一个连通分量*/
//u当前顶点编号,num 连通分量的人数,eWei总边权 ,leNo 老大编号
void DFS(int u,int& num,int& eWei,int& leNo) {
	vis[u]=true;
	num++;
	if(vWeight[u]>vWeight[leNo]) {//在遍历过程中,不断更新老大编号
		leNo=u;
	}

	for(int i=0; i<N; i++) { //遍历与u连通的点
		if(G[u][i]!=INF) {
			eWei+=G[u][i];
			if(vis[i]==false) {
				DFS(i,num,eWei,leNo);
			}
		}
	}


}
/*遍历整个图的连通分量*/
void DfsTraverse() {
	for(int u=0; u<N; u++) {
		if(vis[u]==false) {
			int num=0,eWei=0,leNo=-1;//连通分量的人数,总边权 ,老大编号
			DFS(u,num,eWei,leNo);

			if(num>2&&eWei>K) {
				gangMap.insert({nameMap2[leNo],num});
			}
		}
	}
}

int main() {
	/*初始化*/
	fill(G[0],G[0]+maxN*maxN,INF);
	fill(vis,vis+maxN,false);

	scanf("%d%d\n",&N,&K);
	int no=0;//人名编号
	for(int i=0; i<N; i++) {//插入边权和点权
		string n1,n2;
		int t;
		cin>>n1>>n2;
		scanf("%d",&t);

		if(nameMap.find(n1)==nameMap.end()) { //nameMap中无人名n1
			nameMap[n1]=no;
			nameMap2[no]=n1;
			no++;
		}
		if(nameMap.find(n2)==nameMap.end()) {
			nameMap[n2]=no;
			nameMap2[no]=n2;
			no++;
		}
		G[nameMap[n1]][nameMap[n2]]=t;//无向图边插两次 //是否要累加时间???
		vWeight[nameMap[n1]]+=t;
		vWeight[nameMap[n2]]+=t;
	}

	DfsTraverse();

	cout<<gangMap.size()<<endl;
	for(auto x:gangMap){
		cout<<x.first<<" "<<x.second<<endl; 
	}

	return 0;
}

注意

  • string变量不能直接用scanf输入数据,需要先resize预分配空间;或直接用char[]。

    *不建议用scanf输入string

    //错误(不会报错,但运行程序会有问题)
    string n1,n2;
    int t;
    scanf("%s %s %d\n",&n1,&n2,&t);
    
    //正确一
    string n1,n2;
    n1.resize(3); //需要预分配空间
    n2.resize(3); 
    int t;
    scanf("%s %s %d\n",&n1[0],&n2[0],&t);
    
    //正确二
    char n1[3],n2[3];
    int t;
    scanf("%s %s %d\n",&n1,&n2,&t);
    

  • scanf输入数据注意:

    • 这样的运行结果:(正常)
    scanf("%d%d\n",&N,&K);
    for(int i=0; i<N; i++) {
        string n1,n2;
        int t;
        cin>>n1>>n2;
        scanf("%d",&t);//最后没加\n
        ....
    }
    


    • 这样的运行结果:(错误)需要再打个字符才能继续
    scanf("%d%d\n",&N,&K);
    for(int i=0; i<N; i++) {
        string n1,n2;
        int t;
        cin>>n1>>n2;
        scanf("%d\n",&t);//最后加了\n
        ....
    }
    


  • 二维数组初始化的赋值

    • 这样写是将二维数组的第一个元素G[0][0]赋值为2,其他为0,不知道为什么会记成将数组全部元素赋值为2。。全部元素赋值用memset或fill
    int G[maxN][maxN]= {2};
    
    • 二维数组使用fill:

      注意不能写成 fill(G,G+maxN*maxN,INF)

      fill(G[0],G[0]+maxN*maxN,INF);
      
posted @ 2020-02-07 20:35  musecho  阅读(291)  评论(0编辑  收藏  举报