1034. Head of a Gang (30)

1034. Head of a Gang (30)

时间限制
100 ms
内存限制
65536 kB
代码长度限制
16000 B
判题程序
Standard
作者
CHEN, Yue

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
      这道题最烦人的地方是输入的是英文人名而不是编号,自己写一个hash函数将英文名转换成编号会比较高效,然而PAT这种对效率要求不是太高的考试直接用stl中的map就好了,这里的解决方案是封装一个trans对象,如果map撑不住了再改写接口实现就好了。
      然后抽象一下题意,给你若干顶点及边,每条边对应一个权重,点的权重是与其相连的所有边的权重之和,题目需要你做一下几件事,将图中互相连通的点以集合的形式提取出来,计算每个集合中点的个数,所有边的总权重,及权重最大的点(即题目中所谓的head),找出所有满足点的个数大于2,边的总权重大于k,的集合,先输出满足条件的集合的个数,再将每个集合按照集合的"head"名称的字典序从小到大输出,同时输出这个集合中的人数。
      这道题很显然可以使用图搜索的算法去做,每个点的权重可以很简单地在输入的时候维护,连通域的分割可以通过图的DFS或是BFS做到,每个连通域中点的个数及总权重及权重最大的点也能很简单的在搜索的时候维护。然后,我是用并查集写的这道题目。。。。。。好吧,我以后会补上图的搜索的代码的。
      并查集的概念不了解可以查看鄙人还没有写好的博文,并查集实现及用途总结。不过这个东西网上资料多得是,每个gang中的head很简单地对应到并查集中每个集合的Root,因为题目中要求Head必须是通话时间最长的所以在Union的时候进行一次判断,谁的weight大就选谁为根,同时维护一个sumofweght,计算所有点的权重之和,你也许会说题目中要求的是所有边的权重之和,然而这并没有什么关系,因为每条边被两端的顶点各计算一次,只要将最后结果除二就好了。因为在合并操作的时候需要用到每个点的weight,所以再输入时记录每条边,将合并操作放在输入后进行。最后的排序的话,感觉这种只需要用一次的排序,干脆直接用pair<string,int> + priority_queue解决算了,其他的不管是sort还是map,都需要再来个for循环,而且我最讨厌在电脑上敲出::iterator这样的东西了,还是heap好,直接while(!heap.empty()),咱这叫堆排序,也是排序。
      
# include <iostream>
# include <algorithm>
# include <queue>
# include <cstring>
# include <string>
# include <map>
# include <stack>
using namespace std;

const int _size = 2005;

struct 
{
    map<string,int> str2int;
    map<int,string> int2str;
    string operator[] (int n)
    {
	    return int2str[n];
	}
	int operator[] (string& str) 
	{
		static int cnt = 0;
		if (str2int.find(str)==str2int.end()){
		     str2int[str] = cnt;
			 int2str[cnt] = str;
			 return cnt++;
	    }
	    return str2int[str];
	}
} trans;
struct Disjoint_Set
{
    int father[_size];
    int weight[_size];
    int weight_sum[_size];
    int n;
    Disjoint_Set(int _n):n(_n){memset(father,-1,sizeof(father));memset(weight,0,sizeof(weight));}
    int FindRoot(int i){return father[i]<0?i:(father[i] = FindRoot(father[i]));}
    void AddWeight(int i,int adder) {weight_sum[i] = weight[i] = weight[i] + adder;}
    void Union(int i,int j)
    {
	    if ((i = FindRoot(i))!=(j = FindRoot(j))){
			    if (weight[i]<weight[j]) swap(i,j);
			    father[i] += father[j],father[j] = i;
			    weight_sum[i] += weight_sum[j];
			}
	}
	int getSum(int i){return -father[i];}
};

typedef pair<int,int> line;
typedef pair<string,int> gang;

int main()
{
	int i,j,temp;
	int n,k,total = 0;
	cin >> n >> k;
	Disjoint_Set disj_s((n<<1) + 5);
	stack<line> line_stk;
	for (i=0;i<n;i++)
	{    
	    string a,b;
	    cin >> a >> b >> temp;
	    int na = trans[a];
		int nb = trans[b];
		disj_s.AddWeight(na,temp);
		disj_s.AddWeight(nb,temp);
		line_stk.push(line(na,nb));
		total = max(na,nb);
	}
	n = total+1;
	while (!line_stk.empty())
	{
		line& loca = line_stk.top();
	    disj_s.Union(loca.first,loca.second);
	    line_stk.pop();
	}
	
	priority_queue<gang,vector<gang>,greater<gang> > pri_que;
	int *fath = disj_s.father; 
	int *weight_sum = disj_s.weight_sum;
	for (i=0;i<n;i++)
	    if (fath[i]<-2&&weight_sum[i]/2>k)
	        pri_que.push(gang(trans[i],disj_s.getSum(i)));
    cout << pri_que.size() << endl;
    while (!pri_que.empty())
        {
		    gang loca = pri_que.top();pri_que.pop();
		    cout << loca.first << ' ' << loca.second << endl; 
		}
	return 0;
}
为了方便复习,以后每份代码都配上代码的运行结果,必要的时候配上牛客网的运行结果

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值