【ACM】- PAT. A1034 Head of a Gang 【图的遍历】

博客围绕图中满足条件的连通块数问题展开。分析了题目,如无向图需累加权值,Gang有结点数和总时长要求等。给出解题思路,包括结点名字映射、统计结点时间、用DFS找Gang等,最后提供了AC程序(C++)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接
题目分析

寻找图中满足条件的连通块数;
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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值