时间限制:1s 内存限制:32MB
题目描述
球赛规则:如果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。
- map很好的完成从string到int的映射。map的用法:
map<string, int> M; //定义一个完成从string到int映射的map
M.clear(); //对map中的映射关系清空
M.find(a); //确定map中是否保存string对象a的映射,若没有返回M.end()
M[b] = idx; //若map中不存在string对象b的映射,则定义其映射为idx
idxa = M[a]; //若map中存在string对象a的映射,则读出该映射
- map的内部实现是一棵红黑树。
代码
#include <iostream>
#include <map>
#include <string>
#include <string.h>
using namespace std;
map<string, int> M; //定义一个完成从string到int映射的map
int in[2002]; //保存入度
int main() {
//抽象为拓扑排序,冠军是图中入度为0的点,但要将输入的选手姓名 映射 为节点编号(使用map)
int n;
while(scanf("%d", &n) != EOF && n != 0) {
memset(in, 0, sizeof(in));
M.clear(); //对map中的映射关系清空
int idx = 0; //下一个被映射的数字
for(int i = 0; i < n; i++) {
char str1[50], str2[50];
cin >> 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++;
} else
idxb = M[b];
in[idxb]++; //b的入度加一
}
int cnt = 0;
for(int i = 0; i < idx; i++) {
if(in[i] == 0)
cnt++;
}
puts(cnt==1 ? "Yes" : "No");
}
return 0;
}