1034 Head of a Gang (30 分)

跟着柳婼学姐学习的笔记ヾ(≧▽≦*)o


原题目: 1034 Head of a Gang (30 分).

题意

警察通过犯罪团伙成员间的通话时长来判断头目。
一个犯罪团伙至少有3人,彼此间的总权值(通话时长)大于给定阈值K。每个团伙中,总权值最高的为头目。
给出通话次数N和权值阈值K(均小于等于1000);接着N行,给出通话双方姓名(三个大写字母组成)以及通话时长(不大于1000min的正整数)
① 计算团伙数量,
② 以及每个团伙的头目姓名和成员总数(按头目姓名的字母序排序)。

分析

  1. 题意分析:一个团伙只有一个头目,犯罪分子不会同时在多个团伙中——图的问题;且是判断无向图的连通分量个数的问题,用DFS遍历;也可以用 并查集 解决(总是保持结点权值更大的结点为集合的根结点)。
  2. 图的存储:顶点数较少,可以用邻接矩阵存储——顶点为人,矩阵元素为每条边的权值
    ——① ⭐⭐为了能利用静态数组的随机访问性,将人名转换成数字ID,存储在 两个map 中——一个按名字查找ID,一个按ID查找名字;ID从1开始,用idNumber计数,顺便统计图的总顶点数,便于后面遍历;② 边的权值用于计算一个团伙的总权值totalw,最后需要判断是否大于阈值K(构成“团伙”的要求之以)。
  3. 寻找头目:设置数组weight,计算顶点权值。
  4. 深度优先搜索法遍历图:
    ① 设置数组visit标记访问情况。
    ② 写一个遍历图的函数DFSTrave(),枚举所有连通分量,调用遍历连通分量中所有顶点的函数DFS,然后将符合团伙(人数>2且总权值>k)的记入map中(再开一个map用于记录答案团伙,用到按值查找名字,键为名字,值为团伙人数)。
    ③ 再写一个遍历连通分量中所有顶点的函数DFS——① 通过 引用 返回各连通分量的头目、人数、总权值;② 特别地,这题除了顶点以外还需要 遍历所有边,为了遍历到且不重复,故遍历过的边的权值设为0。

知识点

  1. 计算无向图的连通分量个数——深度优先搜素法DFS遍历图;且需要遍历所有边——遍历过的边权值设为0。
  2. 写函数给字符串编号,使字符串可以随机访问数组;并用map记录字符串与编号的对应关系可以设置两个——一个按编号查,一个按字符串查)。

词汇

WORDS释义WORDS释义
gang一帮,一伙(罪犯)threshold门槛;阈值

CODE

#include <iostream>
#include <map>
using namespace std;
map<string, int> strToInt;  //把名字换成ID(从1开始)(按名字搜索ID)
map<int, string> intToStr;  //存储ID对应名字(按ID搜索名字)
map<string, int> ans;  //存头目(按名字搜索)
int idNumber = 1;  //总人数;生成ID
int k;  //阈值
int G[2010][2010], weight[2010];  //图的邻接矩阵;顶点总权值
bool visit[2010];  //标记访问情况

int stoifunc(string s);  //返回名字对应ID
void DFS(int u, int &head, int &numMember, int &totalw);  //访问顶点u及其所在连通分量
void DFSTrave();  //遍历所有连通分量

int main()
{
    int n, w;
    cin >> n >> k;
    string s1, s2;
    for ( int i=0; i<n; i++ ){
        cin >> s1 >> s2 >> w;
        //获取名字对应ID
        int id1 = stoifunc(s1);
        int id2 = stoifunc(s2);
        //累计个人总权值
        weight[id1] += w;
        weight[id2] += w;
        //生成邻接矩阵
        G[id1][id2] += w;
        G[id2][id1] += w;
    }
    //遍历图
    DFSTrave();
    //输出
    cout << ans.size() << endl;
    for ( auto it=ans.begin(); it!=ans.end(); it++ )
        cout << it->first << " " << it->second << endl;
    
    return 0;
}
int stoifunc(string s){
    if ( strToInt[s]==0 ){  //没转换过的名字
        strToInt[s] = idNumber;
        intToStr[idNumber] = s;
        return idNumber++;  //返回名字对应ID
    }
    else
        return strToInt[s];  //转换过了直接返回ID
}
void DFS(int u, int &head, int &numMember, int &totalw){ //统计该连通分量的头目、人数、头目总权值
    visit[u] = true;  //标记访问
    numMember++;
    if ( weight[u]>weight[head] )
        head = u;  //顶点总权值大的为头目
    for ( int v=1; v<idNumber; v++ ){
        if ( G[u][v]>0 ){
            totalw += G[u][v];
            G[u][v] = G[v][u] = 0;  //遍历过一条边后就设该边权值为0(防止回路遍历)
            if ( visit[v]==false )
                DFS(v, head, numMember, totalw);
        }
    }
}
void DFSTrave(){
    for ( int i=1; i<idNumber; i++ ){
        if ( visit[i]==false ){  //遍历顶点i及其所处连通分量
            int head = i, numMember = 0, totalw = 0;
            DFS(i, head, numMember, totalw);
            if ( numMember>2 && totalw>k )
                ans[intToStr[head]] = numMember;  //需要符合构成团伙的要求
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值