PAT_1034 Head of a Gang (30 分) (dfs)

题目链接
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

第一步建图,需要进行字符串hash,但是给的字符串由3位大写字母表示,直接按照26进制转换为10进制表示的话最大为262626,顶点编号这么大显然开辟邻接矩阵会爆栈,因此想到直接用map进行映射,使用两个map,第一个map记录字符串到顶点编号的映射,第二个map记录顶点编号到字符串的映射。

建立映射时,顶点编号idx从0开始,如果遇到第一个map中不存在的key,则将此字符串作为键、idx作为顶点编号存入,并id++,最终id其实也就是最终顶点的数目。

特别要注意的是,题目给了n条通话记录,相当于告诉n条边,所以顶点数目至少是2n,用邻接矩阵存图时开辟的空间至少要是2000*2000的,如果误认为最大顶点数目为1000开辟1000 *1000的空间,那么测试点3会发生段错误。

本题适合用邻接矩阵存储图,因为A与B通话,B与A通话如果设计为有向图的两条有向边的话,统计A的权和时不方便,设计为邻接矩阵,则A与B之间有通话,则G[A][B]和G[B][A]都加上该权重,这样就构造的是一个无向简单图了,统计权和更简单。

根据题意其实gang就是一个连通分支,只有该连通分支内总权和大于阈值K并且顶点数目大于2才成为gang,里面的head是权和最大的那个顶点。

寻找一个连通分支中权和最大的顶点时,可以在访问邻接点前定义临时变量tmp存储权和,for循环访问每个邻接点时累加权值,假设当前顶点是v,因为dfs也会递归访问v的邻接点w,因此对w进行dfs也会对w的每个邻接点累加权值,因此在dfs的同时就可以找到这个连通分支权和最大的顶点。

还要计算一个连通分支中所有的边权和,因为有可能成环,比如样例中的HGF,因为有vis标记,当访问回最初的顶点会导致少计算一条边权。我采取的解决方法是:如前所述统计了该连通分支每个顶点的邻接点的权和,把这些权和加起来实际就是总权和的2倍,因为每一条边的权值被统计了两次。

dfs要记得把第一个结点vis置为true!!

#include <iostream>
#include <bits/stdc++.h>
using namespace std;
int G[2005][2005]={0};//0表示不连通
bool vis[1005]={false};
map<string,int> mp;
map<int,string> mpres;
struct RES{
    int v;//记录权重最大的个人的顶点
    string name;
    int weightV;//记录v邻接点权重和
    int count;//记录团伙多少个人
    RES(string _name,int _count){
        name=_name;
        count =_count;
    }
    RES(){
        v=-1;
        weightV =0;
    }
}tmpV;//临时顶点 每次需要更新
int tmpThre,tmpCnt;//统计gang的总和
vector<RES> res;//记录在最终结果

void dfs(int v,int nodeNum){
    //以v开始进行dfs,能统计出能否构成gang,能构成gang就再统计
    int tmp=0;//记录v所有邻接点的权和
    for(int w=0;w<nodeNum;w++){
        tmp+=G[v][w];
        if(!vis[w] && G[v][w]!=0){
            tmpCnt++;
            vis[w]=true;
            dfs(w,nodeNum);
        }
    }
    if(tmpV.v==-1){
        tmpV.v=v;
        tmpV.weightV=tmp;
    }else if(tmpV.weightV < tmp){
        tmpV.v=v;
        tmpV.weightV=tmp;
    }
    tmpThre+=tmp;
}
bool cmp(RES a,RES b){
    return a.name < b.name;
}
int main() {
#ifdef ONLINE_JUDGE
#else
    freopen("1.txt","r",stdin);
#endif
    int N,K;
    cin >> N >> K;
    int idx=0;//也相当于记录顶点个数了
    for(int i=0;i<N;i++){
        string tmp1,tmp2;
        int w;
        cin >> tmp1 >> tmp2 >> w;
        if(mp.find(tmp1)==mp.end()){
            mp[tmp1]=idx++;
            mpres[idx-1]=tmp1;
        }
        if(mp.find(tmp2)==mp.end()){
            mp[tmp2]=idx++;
            mpres[idx-1]=tmp2;
        }
        G[mp[tmp1]][mp[tmp2]]+=w;
        G[mp[tmp2]][mp[tmp1]]+=w;//这样相当于构建了一个无向图
    }
   /* for(int i=0;i<idx;i++){
        for(int j=0;j<idx;j++)
            cout << G[i][j] << " ";
        cout << endl;
    }*/
    int gang=0;
    for(int i=0;i<idx;i++){
        if(!vis[i]){
            vis[i]=true;
            tmpV.v=-1;//先更新一下全局临时变量
            tmpThre=0;
            tmpCnt=1;//自己本身也要算上
            dfs(i,idx);
            //cout << tmpThre << endl;
            if(tmpThre/2>K && tmpCnt>2){
                gang++;
                res.push_back(RES(mpres[tmpV.v],tmpCnt));
            }
        }
    }
    cout << gang << endl;
    sort(res.begin(),res.end(),cmp);
    for(int i=0;i<res.size();i++)
        cout << res[i].name  <<" " << res[i].count << endl;
        return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值