PAT甲级-1034 Head of a Gang

题目

题目大意

一个犯罪团伙满足条件:人数 > 2;团伙内的总通话时长 > k。团伙首领就是该团伙中通话时长最多的。先给定一组通话,格式为 A B time,要求输出犯罪团伙的数目,并输出每个团伙的首领名字和该团伙的人数。如果没有犯罪团伙,就输出0。

思路

大致的思路并不难,但是有许多细节的坑点。求犯罪团伙的数量就是求图的连通分量,确定了这一点后,再考虑图的存储。

该题和别的题不同,不是给下标,而是给名字,并且还有权值,就不能用二维string数组了,只能用邻接表,但邻接表又对应下标。因此需要建立一个从名字到下标的映射,就用map了,既方便图的构建,又方便后续由下标找名字的操作(当然,也可以再建一个从下标到名字的map,后面找名字会更加方便,但是我有点懒没有弄hhh)。

构建图还需要注意一点,就是A给B通话20,B给A通话10,那么A到B和B到A的权值都是30。(这点我调试的时候才发现)

求连通分量的数目用dfs,很经典了。刚开始我在里面加了计算每个连通分量总通话时长的代码,但由于连通分量有可能是环,用dfs会少加一条边,所以就把计算total挪到dfs后面了。找到连通分量后再算这个分量的总权值,为防止重复,加过一条边就把这条边的权值设为0。

找到犯罪团伙的首领后,还需要按照人名的字典序输出,否则测试点2、5错误。所以我又加了个map,map可以自动排序。

测试点3段错误是因为开的数组太小了,无向图最好开到2 * n + 1。

代码

#include <iostream>
#include <vector>
#include <map>
using namespace std;

int n, m;  // 这里我把k换成m了
int g[2001][2001];  // 刚开始开的1001,测试点3段错误,无向图最好开到2*n + 1
map<string, int> mp;  // 名字-下标
map<int, int> res;  // 存储各个连通分量的犯罪首领及团伙人数
bool b[2001] = {false};
vector<int> team;  // 记录每个连通分量的成员
int total = 0;  // 连通分量的总权值

void dfs(int vi){
    b[vi] = true;
    team.push_back(vi);
    for (int i = 1; i <= n; i++){
        if (!b[i] && g[vi][i]){
            dfs(i);
        }
    }
}

int main(){
    cin >> n >> m;
    int temp = 1;  // 记录名字的下标
    for (int i = 0; i < n; i++){
        string v1, v2;
        int time;
        cin >> v1 >> v2 >> time;
        if (i == 0){
            mp[v1] = temp;
            temp++;
            mp[v2] = temp;
            temp++;
        }else{
            int flag1 = 0, flag2 = 0;  // 标记有没有在mp中找到v1,v2
            for (auto it = mp.begin(); it != mp.end(); it++){
                if (v1 == it->first){
                    flag1 = 1;
                }
                if (v2 == it->first){
                    flag2 = 1;
                }
            }
            if (!flag1){
                mp[v1] = temp;
                temp++;
            }
            if (!flag2){
                mp[v2] = temp;
                temp++;
            }
        }
        if (g[mp[v1]][mp[v2]]){
            g[mp[v1]][mp[v2]] += time;
            g[mp[v2]][mp[v1]] += time;
        }else{
            g[mp[v1]][mp[v2]] = g[mp[v2]][mp[v1]] = time;
        }
    }  // 构建图

    for (int i = 1; i <= n; i++){
        if (!b[i]){
            team.clear();
            total = 0;
            dfs(i);
            if ((int)team.size() > 2){
                for (int j = 0; j < (int)team.size(); j++){
                    for (int k = 0; k != j && k < (int)team.size(); k++){
                        total += g[team[j]][team[k]];
                        g[team[j]][team[k]] = 0;  // 加过的边直接设为0
                    }
                }
            }  // 如果是环,total无法在dfs中计算,所以要另外计算total

            if ((int)team.size() > 2 && total > m){
                int maxTime = 0, head;
                for (int j = 0; j < (int)team.size(); j++){
                    int sum = 0;
                    for (int k = 1; k <= n; k++){
                        if (g[team[j]][k]){
                            sum += g[team[j]][k];
                        }
                        if (g[k][team[j]]){
                            sum += g[k][team[j]];
                        }
                    }
                    if (maxTime < sum){
                        maxTime = sum;
                        head = team[j];
                    }
                }  // 找犯罪团伙的首领
                res[head] = (int)team.size();
            }
        }
    }

    if (res.size() == 0){
        cout << "0" << endl;
    }else{
        cout << res.size() << endl;
        map<string, int> result;  // 题目要求犯罪首领输出按字典序(测试点2、5)
        for (auto i = res.begin(); i != res.end(); i++){
            for (auto j = mp.begin(); j != mp.end(); j++){
                if (i->first == j->second){
                    result[j->first] = i->second;
                    break;
                }
            }
        }
        for (auto it = result.begin(); it != result.end(); it++){
            cout << it->first << " " << it->second << endl;
        }
    }

    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_74092648/article/details/142898079