1034 Head of a Gang (30分)

34 篇文章 0 订阅
5 篇文章 0 订阅

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

这道题要做的事有三个,1.因为输入的是三个字母,图的顶点存的应该是序号,所以我们写一个函数把字母转化为某个人的编号。2.遍历所有人,如果当前这个人未被访问,就dfs他的连通块,在dfstrave里定义好当前连通块的head = 当前结点,totalvalue为当前连通块的总时长,numPerson为当前连通块的总人数,如果两个都符合要求,就把head加入到最后的结果里。3.dfs遍历连通块,因为dfstrave里每次dfs都是遍历一个连通块,所以是传上面定义的三个值的引用,需要随时更改。每次dfs就把当前连通块的人数++,并把当前结点标记为已访问。如果当前结点的点权大于现在head的点权,就更新head。然后遍历所有结点,如果是当前结点能到达的结点(也就是这个连通块的结点),连通块的总边长加上当前结点和当前结点可以到达的那个结点的边权。然后将这条双向边都设为0,防止重复计算,因为一个连通块只需要加一次边权即可。如果当前结点能到达的这个结点未被访问过,就dfs这个结点。
因为既要边权又要点权,还要定义一个weight数组来记录点权。
最后输出的是各个头结点的姓名和连通块的结点数,所以定义一个map,分别存放姓名和对应的结点数,在确定某个连通块头结点的时候将头结点和对应的结点数加进去。
最后遍历这个map,有迭代器来遍历,输出it->first和it->second,分别对应key和value

//先求连通分量,再求每个连通分量里点的个数,再判断谁是最大的
//后两个用dfs来解决
#include<iostream>
#include<map>
#include<string>
using namespace std;
const int MAXV = 2010;
int G[MAXV][MAXV] = {0}, weight[MAXV] = {0};
map<string, int> stringToint;
map<int, string> intTostring;
map<string, int> headTonum;
bool vis[MAXV] = {false};
int numPerson = 0, k;//记录一共有多少人,方便给他们一个序号,k是阈值

void dfs(int nowVisit, int& head, int& totalValue, int& numMember){
	numMember++;//成员人数+1
	vis[nowVisit] = true;//标记nowVisit已访问
	if(weight[nowVisit] > weight[head]){
		head = nowVisit;//当前访问结点的点权大于头目的点权,则更新头目 
	} 
	for(int i = 0; i < numPerson; i++){//枚举所有人
		if(G[nowVisit][i]>0){//如果从nowVist能到达i(边权大于0) 
			 totalValue += G[nowVisit][i];//连通块的总边权增加该边权
			 G[nowVisit][i] = G[i][nowVisit] = 0;//删除这条边(将i到nowVisit的值设为0),防止重复计算
			 if(vis[i] == false){//如果i未被访问,则递归访问i
			 	dfs(i, head, totalValue, numMember); 
			 } 
		} 
	} 
} 
void DFSTravese(){
    for(int i = 0; i < numPerson; i++){//遍历所有人
        if(vis[i] == false){
            int head = i, totalValue = 0, numMember = 0;
            dfs(i, head, totalValue, numMember);
            if(totalValue > k && numMember > 2){//一个连通块的总时间大于k,并且人数大于2,这个连通块的head就是一个头目
                headTonum[intTostring[head]] = numMember;
            }
        }
    }
}

//change函数返回姓名str对应的编号
int change(string str){
	if(stringToint.find(str) != stringToint.end()){//如果str已经出现过 
		return stringToint[str];//返回编号 
	}
	else{
		stringToint[str] = numPerson;//str的编号为numPerson
		intTostring[numPerson] = str;//numPerson对应str
		return numPerson++;//总人数++,返numPerson,然后numPerson=numPerson+1 
	} 
} 

int main(){
    int n, w;
    cin>>n>>k;
    string s1, s2;
    for(int i = 0; i < n; i++){
        cin>>s1>>s2>>w;
        int id1 = change(s1);
        int id2 = change(s2);
        G[id1][id2] += w;
        G[id2][id1] += w;
        weight[id1] += w;
        weight[id2] += w;
    }
    DFSTravese();
    cout<<headTonum.size()<<endl;
    for(auto it= headTonum.begin(); it != headTonum.end(); it++){
        cout<<it->first<<" "<<it->second<<endl;
    }
    return 0;
}

一刷直接抄的,二刷边抄边写,这种程度的dfs我就不行了。dfs引用传参是很好用的,特别是和连通分量有关的题里面,这里用了引用传参,因为在main里面一次dfs就遍历了一整个连通块,我们需要知道这个连通块里的head,也就是通话时长最多的那个。为此我们在一开始就要定义一个weight数组,记录每个人的总通话时长,在dfs里,如果当前这个人的weight>当前块head的通话时长,head换人。然后遍历总人数,如果和当前结点有通话记录,就让totalvalue加上这段通话记录,totalvalue是用来记录当前连通块的总通话时长的,防止重复计算,加上一次后,将当前这个人和i的记录双向删除。如果i未被访问,就dfs它,每次dfs记得将块的总人数++。
在main的里,每次都重新定义人数,head和totalvalue,然后传引用,这样就可以随机记录了

//用图来存两个人之间的通话时长,用一个dfs来求连通块个数,
#include<algorithm>
#include<iostream>
#include<map>
#include<unordered_map>
using namespace std;
const int maxn = 2010;//n组通话,所以人数可能是2*n,要定义两倍的最大人数
int n,k;
int index = 0;
int G[maxn][maxn];
int weight[maxn] = {0};
unordered_map<string,int> stringToint;//string转化为int
unordered_map<int,string> intTostring;//int转化为string
map<string,int> Gang;//每个集群首领对应的人数
bool vis[maxn] = {false};

void dfs(int nowVis, int& head, int &totalValue, int &numberPerson){
    vis[nowVis] = true;
    numberPerson++;
    if(weight[head] < weight[nowVis]){
        head = nowVis;
    }
    for(int i = 0; i < index; i++){
        if(G[nowVis][i] > 0){
            totalValue += G[nowVis][i];
            G[nowVis][i] = G[i][nowVis] = 0;
            if(vis[i] == false) dfs(i,head,totalValue,numberPerson);
        }
    }
}


int main(){
    fill(G[0],G[0]+maxn*maxn,0);
    cin>>n>>k;//k是阈值
    string u,v;
    int time;
    for(int i = 0; i < n; i++){
        cin>>u>>v>>time;
        if(stringToint.find(u) == stringToint.end()){
            stringToint[u] = index;
            intTostring[index++] = u;
        }
        if(stringToint.find(v) == stringToint.end()){
            stringToint[v] = index;
            intTostring[index++] = v;
        }
        int id1 = stringToint[u];
        int id2 = stringToint[v];
        G[id1][id2] += time;
        G[id2][id1] += time;
        weight[id1] += time;
        weight[id2] += time;
    }
    for(int i = 0; i < index; i++){
        if(vis[i] == false){
            int head = i, totalValue = 0, numberPerson = 0;
            dfs(i, head, totalValue,numberPerson);
            if(totalValue > k && numberPerson >2){
                Gang[intTostring[head]] = numberPerson;
            }
        }
    }
    cout<<Gang.size()<<endl;
    map<string, int>::iterator it;//auto it也行
	for(it = Gang.begin(); it != Gang.end(); it++){//遍历所有Gang
		cout<<it->first<<" "<<it->second<<endl; 
	} 
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值