题目链接
题目分析
寻找图中满足条件的连通块数;
1、无向图,需要多次累加权值
2、Gang: 结点数大于2 且 总时长 > K
3、head: Gang中时长最大的结点
4、题目保证每个Gang中head唯一
5、输出按head字典序排序
6、通话记录上限为1000,则结点数上限为2000(因此最好用邻接表实现)
解题思路
1、结点名字:map<string, int>
映射到结点序号,之后访问节省时间
2、输入时统计每个结点的时间(权重)
3、一次DFS可能得到一个Gang(连通块)
4、注意:即使是已经访问过的结点,边的权值还是要累加,但结点不再DFS遍历;可是这样可能又会导致
某些边被重复累加;因此统计完时间就删除此边权值(因为上一步的累加即使是访问过的结点也会执行)
AC程序(C++)
/**********************************
*@Author: 3stone
*@ACM: PAT.A1034 Head of a Gang
*@Time: 18/8/12
*@IDE: VSCode 2018 + clang++
***********************************/
#include<cstdio>
#include<cstring>
#include<string>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;
const int maxn = 2010;
int N, K, sum, M; //边数,最大传播深度, 转发总数, 结点数
int head, head_time; //Gang中的head 及其 时长
int total_time, num; //Gang的总时长, Gang中的人数
int G[maxn][maxn]; //邻接矩阵
int weight[maxn];
bool vis[maxn] = {false}; //标记结点是够进入队列
struct Gang{
char name[5];
int count;
bool operator < (const Gang temp) const {//当然也可以用函数外的比较函数/map<name, num>自动排序
return strcmp(this->name, temp.name) < 0;
}
}gang[maxn];
void DFS(int u) {
vis[u] = true;
num++;
if(weight[u] > head_time) {
head = u;
head_time = weight[u];
}
for(int i = 1; i <= M; i++){
if(G[u][i] != 0) {
total_time += G[u][i]; //即使是已经访问过的结点,边的权值还是要累加,但不再DFS遍历
G[u][i] = G[i][u] = 0; //统计完时间就删除此边,防止重复计算时间!!!(因为上一步的累加即使是访问过的结点也会执行)
if(vis[i] == false)
DFS(i);
}
}
}
int main() {
char user1[4], user2[4];
int tm;
while(scanf("%d%d", &N, &K) != EOF) {
map<string, int> name_to_key; //结点名->序号 (也可用 结点结构体操作)
map<int, string> key_to_name; //序号->结点名
int key = 1;
//初始化 所有结点无连接
for(int i = 0; i < maxn; i++)
for(int j = 0; j < maxn; j++)
G[i][j] = 0; //本题为无权图
for(int i = 0 ; i < maxn; i++){ //每个结点初始时长为0
weight[i] = 0;
gang[i].count = 0;
}
//输入图信息
for(int i = 1; i <= N; i++) { //输入边的信息
scanf("%s %s", user1, user2);
if(name_to_key.find(user1) == name_to_key.end()){
name_to_key[user1] = key;
key_to_name[key++] = user1;
}
if(name_to_key.find(user2) == name_to_key.end()){
name_to_key[user2] = key;
key_to_name[key++] = user2;
}
scanf("%d", &tm);
G[name_to_key[user1]][name_to_key[user2]] += tm; //边时间(无向图)
G[name_to_key[user2]][name_to_key[user1]] += tm;
weight[name_to_key[user1]] += tm; //每个用户的时间(出边&入边都算)
weight[name_to_key[user2]] += tm;
}
M = key - 1; //记录结点数
int gang_num = 0;
//遍历图
for(int i = 1; i <= N; i++){
if(vis[i] == false){
head_time = -1; //记录head的市场
head = 1; //记录head
total_time = 0; //记录此连通块的总时间
num = 0;
DFS(i);
if(num > 2 && total_time > K){ //形成一个Gang
gang[gang_num].count = num;
strcpy(gang[gang_num++].name, key_to_name[head].c_str());
}
}
}
sort(gang, gang + gang_num);
printf("%d\n", gang_num);
for(int i = 0; i < gang_num; i++){
printf("%s %d\n", gang[i].name, gang[i].count);
}
}//while
return 0;
}