当前位置: 代码迷 >> 综合 >> JakeLin-[蓝桥杯]卡勒沃夫之弱水路三千(提高型)-拓扑排序-题解
  详细解决方案

JakeLin-[蓝桥杯]卡勒沃夫之弱水路三千(提高型)-拓扑排序-题解

热度:55   发布时间:2023-11-13 15:18:20.0

题目描述

锦瑟年华谁与度  莫问情归处  只影向斜阳  剑吼西风  欲把春留驻 
天涯芳草无归路  回首花无数  解语自销魂  弱袂萦春  尘缘不相误 
...... 
在卡勒沃夫充满文学杀伤力的声音中,身处紫荆2号楼202B的四位远近高低各不同的室友纷纷回忆起了各自波澜起伏的过去,并对长在百草园,邻有百花谷的现状表达了各自的见解。 
某Q:" ...我小学就开窍了...她的父母说我很好,但是...今天又和北林的联系了..." 
某X:" ...差点就成了,结果到学校了...这个方法放假了我去对我的同桌用!..." 
某W:" ..." (千言万语不言中,有大量的故事等待考古) 
某Z:" ...为了来清华...咱们审美观不一样,不会抢..." 
...... 
卡勒沃夫在这个不朽的夜话中搜集出了某人零散的历任女友资料,为了强迫某人将他出的题目的标程交出,现在卡勒沃夫需要一个能将这些零散信息整合起来的  程序。伴随着雄壮委婉动人的音乐,身为程序设计快男(超女)的你降临了!卡勒沃夫正对着您做Orz状并请求着:" 神牛啊~请施舍给我一段程序把~偶米头  发~" 。。 

输入

第一行为一个不超过5的整数T,表示数据的组数。之后每组数据的一行为一个不超过100的整数n。之后n行每行有两个用单个空格隔开的字符串(每个字符串只有英文大小写字母,长度不超过10),为两位mm的名字。每行第一个mm先于第二个mm成为某人的女友。 
在这里我们假装诅咒某人不会同时被两个或两个以上mm泡,某个mm抛弃了某人后不会再吃回头草,同时卡勒沃夫深邃的洞察力使得他收集到了充足的信息以确定某人女友的先后顺序。 

在小数据组中出现的人物不超过13个

输出

输出T行,每行对应一组数据,并按照mm们从先到后成为某人女友的顺序输出她们的名字,各个名字间用一个空格隔开。 

样例输入

2 
2 
RY  Unknown 
YSZ  RY 
3 
tomorrow  yestoday 
tomorrow  today 
today  yestoday 

样例输出

YSZ RY Unknown
tomorrow today yestoday

原题链接:[蓝桥杯][算法提高VIP]卡勒沃夫之弱水路三千(提高型)

 

本题考点为拓扑排序,题例如下图:

in表示入度 out表示出度

一、思路

所谓拓扑排序,就是要从拓扑图中找出三个节点的先后顺序,应为:
tomorrow->today->yesterday


拓扑排序思路相信大家很熟悉了:

  • 找到起点:入度为0的节点(tomorrow)
  • 访问它(输出它),并将其两个出度边删除(yesterday和today入度减1)
  • 找到下一个入度为0的节点,循环往复

二、实现

1.节点结构体:

struct node{string s;   //节点名称int in,out;  //入度和出度
}nn[maxn];

nn[cnt]为一个结构体数组,其每一个元素为一个节点。

2.存储地图:

map<string,int> nameToindex; //需了解map和vector本质跟数组一样
vector<int> mapper[maxn];
int vis[maxn];

变量解释:
nameToindex[“today”] = 2:表示名字为today的节点是第2个节点
mapper[2]:是第2个节点所指向的节点组成的数组
vis[2]:表示第2个节点访问过了后续无需访问

3.构建地图需注意:
第一次出现这个名字,需新增一个节点,出入度默认为0。
第n次出现这个名字(n>1),只需更新这个节点的出入度即可。

完整代码如下:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<map>; 
using namespace std;
const int maxn = 100 + 10;
struct node{string s;int in,out;  //入度和出度
}nn[maxn];
map<string,int> nameToindex;
vector<int> mapper[maxn];
int vis[maxn];
int cnt;
int main(){int n;cin>>n;while(n--){cnt=0; //由于有多组数据,以下四行进行全局变量的初始化memset(vis,0,sizeof(vis));memset(mapper,0,sizeof(mapper));nameToindex.clear();int m;cin>>m;for(int i=0;i<m;i++){string s1,s2;cin>>s1>>s2;int tag1,tag2;tag1=tag2=0;for(int i=0;i<cnt;i++){//不是第一次出现这个节点,更新节点if(nn[i].s == s1){tag1=1;nn[i].out++;}if(nn[i].s == s2){tag2=1;nn[i].in++;}}if(!tag1){//第一次出现名字,新建节点nn[cnt].s = s1;nn[cnt].out++;nameToindex[s1] = cnt;cnt++; }if(!tag2){nn[cnt].s = s2;nn[cnt].in++;nameToindex[s2] = cnt;cnt++;}mapper[nameToindex[s1]].push_back(nameToindex[s2]);//构建地图(以节点的序号,而不是节点的名字)}while(m--){  //有m个节点要访问,循环m次for(int i=0;i<cnt;i++){  //每次找出入度为0的节点,访问并删去输出边if(vis[i]==0 && nn[i].in==0){cout<<nn[i].s<<" ";vis[i]=1;for(int j=0;j<mapper[i].size();j++){ //被访问的点所连接的边入度减1nn[mapper[i][j]].in--;}                    }}    }cout<<endl;}return 0;
}