产生冠军 - 九度教程第 105 题
题目
时间限制:1 秒 内存限制:32 兆 特殊判题:否
题目描述:
有一群人,打乒乓球比赛,两两捉对撕杀,每两个人之间最多打一场比赛。球赛的规则如下:如果 A 打败了 B,B 又打败了 C,而 A 与 C 之间没有进行过比赛,那么就认定,A 一定能打败 C。如果 A 打败了 B,B 又打败了 C,而且,C 又打败了 A,那么 A、B、C 三者都不可能成为冠军。
根据这个规则,无需循环较量,或许就能确定冠军。你的任务就是面对一群比赛选手,在经过了若干场撕杀之后,确定是否已经实际上产生了冠军。
输入:
输入含有一些选手群,每群选手都以一个整数 n(n<1000)开头,后跟 n 对选手的比赛结果,比赛结果以一对选手名字(中间隔一空格)表示,前者战胜后者。
如果 n 为 0,则表示输入结束。
输出:
对于每个选手群,若你判断出产生了冠军,则在一行中输出“Yes”,否则在
一行中输出“No”。
样例输入:
3
Alice Bob
Smith John
Alice Smith
5
a c
c d
d e
b e
a d
0
样例输出:
Yes
No
这便是在图论中所讨论过的拓扑排序问题。将选手对应结点,胜负关系对应为结点之间的有
向边,可以产生冠军的情况即为全图中入度为零的点唯一。与普通的拓扑排序问题不同,这里需要将输入的选手姓名映射为结点编号,这就需要标准对象map。
#include <stdio.h>
#include <vector>
#include <map>//要使用map必须包含此头文件
#include <string>
#include <queue>
using namespace std;
map<string,int> M;//定义一个完成从string到int映射的map
int in[2002];
int main()
{
int n;
while(scanf("%d",&n)!=EOF && n!=0){
for(int i=0;i<2*n;i++){//n组胜负关系,至多存在n个队伍
in[i]=0;//初始化入度
}
M.clear();//对map中的映射关系清空
int idx=0;//下一个被映射的数字
for(int i=0;i<n;i++){
char str1[50],str2[50];
scanf("%s%s",str1,str2);//输入两个选手名称
string a=str1,b=str2;//将字符串保存至string中
int idxa,idxb;
if(M.find(a)==M.end()){//若map中尚无对该a的映射
idxa=idx;
M[a]=idx++;//设定其映射为idx,并递增idx
}else{
idxa=M[a];//否则直接读出该映射
}
if(M.find(b)==M.end()){
idxb=idx;
M[b]=idx++;
//map映射值从1开始计数,
//相比入度数组in索引加1
}else{
idxb=M[b];//确定b的映射,方法与a相同
}
in[idxb]++;//b的入度递增
}
int cnt=0;
for(int i=0;i<idx;i++){
//确定所有映射数字的入度,
//统计入度为0的个数
if(in[i]==0)cnt++;
}
puts(cnt==1 ? "Yes" : "No");//若入度为0输出Yes,否则输出No
}
return 0;
}
如题所示,map很好的完成了从string到int的映射,即完成了选手姓名到结点编号的映射。下面回顾它的用法:
map<string,int> M;//定义一个完成从string到int映射的map
M.clear(); //清空一个map
M.find(b);//确定map中是否保存string对象b的映射,若没有函数返回M.end()
M[b] = idx; //若map中不存在string对象b的映射,则定义b映射为idx
idxb = M[b]; //若map中存在string对象b的映射,则读出该映射
在了解了以上map的用法和用处后,就能使用map完成特定类型变量之间的映射,而不需作过多的干预。顺便一提的是,map的内部实现是一棵红黑树。