文章目录
L1-1 重要的话说三遍
这道超级简单的题目没有任何输入。
你只需要把这句很重要的话 —— “I’m gonna WIN!”——连续输出三遍就可以了。
注意每遍占一行,除了每行的回车不能有任何多余字符。
解题思路
思路?没有思路
#include <iostream>
using namespace std;
int main() {
for (int i = 0; i < 3; i++) {
cout << "I'm gonna WIN!" << endl;
}
return 0;
}
L1-2 日期格式化(C++,字符串)
世界上不同国家有不同的写日期的习惯。比如美国人习惯写成“月-日-年”,而中国人习惯写成“年-月-日”。下面请你写个程序,自动把读入的美国格式的日期改写成中国习惯的日期。
输入格式:
输入在一行中按照mm-dd-yyyy
的格式给出月、日、年。题目保证给出的日期是
1900
1900
1900年元旦至今合法的日期。
输出格式:
在一行中按照yyyy-mm-dd
的格式给出年、月、日。
输入样例:
03-15-2017
输出样例:
2017-03-15
解题思路:
利用字符串提供的find()
方法截取年月日的字符串,然后改变顺序输出
或者也可以把年月日看成数字输入,利用#include <iomanip>
格式化输出
这里采用第一种思路
#include <iostream>
#include <string>
using namespace std;
int main() {
string str;
cin >> str;
string y, m, d;
int index = str.find('-');
int index2 = str.find('-', index + 1);
m = str.substr(0, 2);
d = str.substr(index + 1, 2);
y = str.substr(index2 + 1, 4);
cout << y << '-' << m << '-' << d << endl;
return 0;
}
L1-3 大笨钟(C++,字符串)
微博上有个自称“大笨钟V”的家伙,每天敲钟催促码农们爱惜身体早点睡觉。不过由于笨钟自己作息也不是很规律,所以敲钟并不定时。一般敲钟的点数是根据敲钟时间而定的,如果正好在某个整点敲,那么“当”数就等于那个整点数;如果过了整点,就敲下一个整点数。另外,虽然一天有 24 24 24小时,钟却是只在后半天敲 1 1 1~ 12 12 12下。例如在 23 : 00 23:00 23:00敲钟,就是“当当当当当当当当当当当”,而到了 23 : 01 23:01 23:01就会是“当当当当当当当当当当当当”。在午夜 00 : 00 00:00 00:00到中午 12 : 00 12:00 12:00期间(端点时间包括在内),笨钟是不敲的。
下面就请你写个程序,根据当前时间替大笨钟敲钟。
输入格式:
输入第一行按照hh:mm
的格式给出当前时间。其中hh
是小时,在
00
00
00到
23
23
23之间;mm
是分钟,在
00
00
00到
59
59
59之间。
输出格式:
根据当前时间替大笨钟敲钟,即在一行中输出相应数量个Dang
。如果不是敲钟期,则输出:
Only hh:mm. Too early to Dang.
其中hh:mm
是输入的时间。
输入样例1:
19:05
输出样例1:
DangDangDangDangDangDangDangDang
输入样例2:
07:05
输出样例2:
Only 07:05. Too early to Dang.
解题思路:
采用
C
+
+
C++
C++的方式cin >> num1; getchar(); cin >> num2;
或者
C
C
C的方式scanf("%d:%d", num_1, num2)
均可
然后比较大小判断情况,最后for
循环输出即可
#include <iostream>
#include <iomanip>
using namespace std;
int main() {
int hour, minute;
cin >> hour; getchar(); cin >> minute;
if (hour < 12 || hour == 12 && minute == 0) {
cout << "Only ";
cout << setw(2) << setfill('0') << setiosflags(ios::right) << hour;
cout << ':';
cout << setw(2) << setfill('0') << setiosflags(ios::right) << minute;
cout << ". Too early to Dang." << endl;
}
else {
if (minute) hour++;
hour -= 12;
for (int i = 0; i < hour; i++) {
cout << "Dang";
}
}
return 0;
}
L1-4 拯救外星人(C++,数学)
你的外星人朋友不认得地球上的加减乘除符号,但是会算阶乘 —— 正整数 N N N 的阶乘记为 “ N ! N! N!”,是从 1 1 1 到 N N N 的连乘积。所以当他不知道“ 5 + 7 5+7 5+7”等于多少时,如果你告诉他等于“ 12 ! 12! 12!”,他就写出了“ 479001600 479001600 479001600”这个答案。
本题就请你写程序模仿外星人的行为。
输入格式:
输入在一行中给出两个正整数 A A A 和 B B B。
输出格式:
在一行中输出 ( A + B ) (A+B) (A+B) 的阶乘。题目保证 ( A + B ) (A+B) (A+B) 的值小于 12 12 12。
输入样例:
3 6
输出样例:
362880
解题思路:
也不太清楚这到底是会算术还是不会
考察的知识点只有一个,就是阶乘的计算
采用for
循环实现即可,由于本题数据比较水,甚至可以采用递归
#include <iostream>
using namespace std;
long long factorial(int n) {
long long ret = 1;
for (long long i = 1; i <= (long long)(n); i++) {
ret *= i;
}
return ret;
}
int main() {
int a, b;
cin >> a >> b;
cout << factorial(a + b) << endl;
return 0;
}
L1-5 个位数统计(C++,字符串)
给定一个 k k k 位整数 N = d k − 1 1 0 k − 1 + ⋯ + d 1 1 0 1 + d 0 ( 0 ≤ d i ≤ 9 , i = 0 , ⋯ , k − 1 ) N=d_{k−1}10^{k−1}+⋯+d_110^1+d_0 (0≤d_i≤9, i=0,⋯,k−1) N=dk−110k−1+⋯+d1101+d0(0≤di≤9,i=0,⋯,k−1),请编写程序统计每种不同的个位数字出现的次数。例如:给定 N = 100311 N=100311 N=100311,则有 2 2 2 个 0 0 0, 3 3 3 个 1 1 1,和 1 1 1 个 3 3 3。
输入格式:
每个输入包含 1 1 1 个测试用例,即一个不超过 1000 1000 1000 位的正整数 N N N。
输出格式:
对
N
N
N 中每一种不同的个位数字,以 D:M
的格式在一行中输出该位数字 D
及其在
N
N
N 中出现的次数 M
。要求按 D
的升序输出。
输入样例:
100311
输出样例:
0:2
1:3
3:1
解题思路:
将数字串看作字符串读入,然后在数组中累计数字数量,最后输出数字数量不为 0 0 0的即可
#include <iostream>
#include <string>
using namespace std;
int num_arr[10];
int main() {
string str;
cin >> str;
for (int i = 0; i < int(str.size()); i++) {
num_arr[str[i] - 48]++;
}
for (int i = 0; i < 10; i++) {
if (num_arr[i]) {
cout << i << ':' << num_arr[i] << endl;
}
}
return 0;
}
L1-6 正整数A+B(C++,字符串)
题的目标很简单,就是求两个正整数A
和B
的和,其中A
和B
都在区间
[
1
,
1000
]
[1,1000]
[1,1000]。稍微有点麻烦的是,输入并不保证是两个正整数。
输入格式:
输入在一行给出A
和B
,其间以空格分开。问题是A
和B
不一定是满足要求的正整数,有时候可能是超出范围的数字、负数、带小数点的实数、甚至是一堆乱码。
注意:我们把输入中出现的第
1
1
1个空格认为是A
和B
的分隔。题目保证至少存在一个空格,并且B
不是一个空字符串。
输出格式:
如果输入的确是两个正整数,则按格式A + B = 和
输出。如果某个输入不合要求,则在相应位置输出?
,显然此时和也是?
。
输入样例1:
123 456
输出样例1:
123 + 456 = 579
输入样例2:
22. 18
输出样例2:
? + 18 = ?
输入样例3:
-100 blabla bla...33
输出样例3:
? + ? = ?
解题思路:
考察数据的读入操作
因为以第一个空格为分隔,之前为A
,之后所有的都为B
所以采用cin >> A; cin.ignore();
和getline(cin, B)
读入即可
*注:一定要有cin.ignore()
忽略掉一个字符,否则getline()
读入的字符串首为空格,就需要再去处理一下
然后用代码检测读入的字符串是否是一个符合要求的数字
(1)长度为 1 1 1~ 4 4 4
(2)由 0 0 0~ 9 9 9组成
(3)开头不能为 0 0 0
(4)范围在 [ 1 , 1000 ] [1,1000] [1,1000]
#include <iostream>
#include <string>
using namespace std;
int main() {
string str1, str2;
int a = -1, b = -1;
cin >> str1;
cin.ignore();
getline(cin, str2);
int len1 = str1.size();
int len2 = str2.size();
if (len1 <= 4 && len1 > 0) {
if (str1[0] > '0' && str1[0] <= '9') {//1~9
a = str1[0] - '0';
for (int i = 1; i < len1; i++) {
if (str1[i] >= '0' && str1[i] <= '9') {//0~9
a = a * 10 + str1[i] - '0';
}
else {
a = -1;
break;
}
}
}
}
if (len2 <= 4 && len2 > 0) {
if (str2[0] > '0' && str2[0] <= '9') {//1~9
b = str2[0] - '0';
for (int i = 1; i < len2; i++) {
if (str2[i] >= '0' && str2[i] <= '9') {//0~9
b = b * 10 + str2[i] - '0';
}
else {
b = -1;
break;
}
}
}
}
if (a <= 0 || a > 1000) {//超范围
a = -1;
cout << '?';
}
else cout << a;
cout << " + ";
if (b <= 0 || b > 1000) {//超范围
b = -1;
cout << '?';
}
else cout << b;
cout << " = ";
if (a < 0 || b < 0) cout << '?' << endl;
else cout << a + b << endl;
return 0;
}
L1-7 打印沙漏(C++,二分)
本题要求你写个程序把给定的符号打印成沙漏的形状。例如给定17个“*
”,要求按下列格式打印
*****
***
*
***
*****
所谓“沙漏形状”,是指每行输出奇数个符号;各行符号中心对齐;相邻两行符号数差 2 2 2;符号数先从大到小顺序递减到 1 1 1,再从小到大顺序递增;首尾符号数相等。
给定任意 N N N个符号,不一定能正好组成一个沙漏。要求打印出的沙漏能用掉尽可能多的符号。
输入格式:
输入在一行给出 1 1 1个正整数 N N N( ≤ 1000 ≤1000 ≤1000)和一个符号,中间以空格分隔。
输出格式:
首先打印出由给定符号组成的最大的沙漏形状,最后在一行中输出剩下没用掉的符号数。
输入样例:
19 *
输出样例:
*****
***
*
***
*****
2
解题思路:
只要知道了一行中最多含有多少个给定的符号,剩下的就是简单的格式化输出了
因为注意到等差数列的存在,采用求和公式和二分搜索得到沙漏的一半有多少行
#include <iostream>
using namespace std;
//计算符号数量
inline int sum(int n) {//n为一半行数
return 2 * n * n - 1;
}
//计算最多几行输出(一半)
int bin_search(int n) {//n为可用符号数
int l = 0, r = 1000, m;
while (l + 1 != r) {
m = (l + r) / 2;
if (sum(m) > n) {//不可行
r = m;
}
else {//可行
l = m;
}
}
return l;
}
char format[1024];
int main() {
int n;
char sign;
cin >> n; cin >> sign;
int ret = bin_search(n); //最多一半行数
int sign_num = ret * 2 - 1; //最多一行符号数
int left = n - sum(ret); //剩余符号数
int l = 0, r = sign_num - 1;
for (int i = 0; i < sign_num; i++) format[i] = sign; //初始化格式
for (int i = 0; i < ret * 2 - 1; i++) {
cout << format << endl;
//准备下一次输出
if (l < r) {
format[l] = ' '; format[r] = '\0';
l++; r--;
}
else {
l++; r--;
format[l] = format[r] = sign;
}
}
cout << left << endl;
return 0;
}
L1-8 机工士姆斯塔迪奥(C++,图)
在 M M O R P G MMORPG MMORPG《最终幻想14》的副本“乐欲之所瓯博讷修道院”里, B O S S BOSS BOSS 机工士姆斯塔迪奥将会接受玩家的挑战。
你需要处理这个副本其中的一个机制: N × M N×M N×M 大小的地图被拆分为了 N × M N×M N×M 个 1 × 1 1×1 1×1 的格子, B O S S BOSS BOSS 会选择若干行或/及若干列释放技能,玩家不能站在释放技能的方格上,否则就会被击中而失败。
给定 B O S S BOSS BOSS 所有释放技能的行或列信息,请你计算出最后有多少个格子是安全的。
输入格式:
输入第一行是三个整数 N , M , Q N,M,Q N,M,Q ( 1 ≤ ∗ N ∗ × ∗ M ∗ ≤ 1 0 5 1≤*N*×*M*≤10^5 1≤∗N∗×∗M∗≤105, 0 ≤ Q ≤ 1000 0≤Q≤1000 0≤Q≤1000),表示地图为 N N N 行 M M M 列大小以及选择的行/列数量。
接下来 Q Q Q 行,每行两个数 T i , C i T_i,C_i Ti,Ci,其中 T i = 0 T_i=0 Ti=0 表示 B O S S BOSS BOSS 选择的是一整行, T i = 1 T_i=1 Ti=1 表示选择的是一整列, C i C_i Ci 为选择的行号/列号。行和列的编号均从 1 1 1 开始。
输出格式:
输出一个数,表示安全格子的数量。
输入样例:
5 5 3
0 2
0 4
1 3
输出样例:
12
解题思路:
应该没有人会直接开二维数组吧
因为 B O S S BOSS BOSS一次只会选中某行或者某列,所以我们也以行和列为单位进行操作
开两个一维数组存储行和列被选中的状态,然后二重循环遍历并判断累计即可
#include <iostream>
using namespace std;
const int max_n = 1e5;
bool row[max_n + 1], col[max_n + 1];
int main() {
int n, m, q;
cin >> n >> m >> q;
int select, oper_num;
for (int i = 0; i < q; i++) {
cin >> select >> oper_num;
if (select == 0) row[oper_num] = true;
else col[oper_num] = true;
}
long long sum = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (!row[i] && !col[j]) {
sum++;
}
}
}
cout << sum << endl;
return 0;
}
L2-1 排座位(C++,并查集)
布置宴席最微妙的事情,就是给前来参宴的各位宾客安排座位。无论如何,总不能把两个死对头排到同一张宴会桌旁!这个艰巨任务现在就交给你,对任何一对客人,请编写程序告诉主人他们是否能被安排同席。
输入格式:
输入第一行给出
3
3
3个正整数:N
(
≤
100
≤100
≤100),即前来参宴的宾客总人数,则这些人从
1
1
1到N
编号;M
为已知两两宾客之间的关系数;K
为查询的条数。随后M
行,每行给出一对宾客之间的关系,格式为:宾客1 宾客2 关系
,其中关系
为
1
1
1表示是朋友,
−
1
-1
−1表示是死对头。注意两个人不可能既是朋友又是敌人。最后K
行,每行给出一对需要查询的宾客编号。
这里假设朋友的朋友也是朋友。但敌人的敌人并不一定就是朋友,朋友的敌人也不一定是敌人。只有单纯直接的敌对关系才是绝对不能同席的。
输出格式:
对每个查询输出一行结果:如果两位宾客之间是朋友,且没有敌对关系,则输出No problem
;如果他们之间并不是朋友,但也不敌对,则输出OK
;如果他们之间有敌对,然而也有共同的朋友,则输出OK but...
;如果他们之间只有敌对关系,则输出No way
。
输入样例:
7 8 4
5 6 1
2 7 -1
1 3 1
3 4 1
6 7 -1
1 2 1
1 4 1
2 3 -1
3 4
5 7
2 3
7 2
输出样例:
No problem
OK
OK but...
No way
解题思路:
很容易发现朋友关系具有传递性,采用并查集来维护朋友关系
因为数据量比较小,可以直接用二维数组维护敌人关系
之后的操作就很简单了:
(1)首先判断是不是敌人
(2)然后判断是不是朋友
(3)最后从四种输出中选择一种即可
AC代码如下
#include <iostream>
using namespace std;
const int max_n = 100;
int n, m, k;
int fa[max_n + 1];
int enemy[max_n + 1][max_n + 1];
int find(int x) {
return x == fa[x] ? x : (fa[x] = find(fa[x]));
}
bool is_in_same(int x, int y) {
x = find(x); y = find(y);
if (x == y) return true;
else return false;
}
void merge(int x, int y) {
x = find(x); y = find(y);
fa[x] = y;
}
int main() {
cin >> n >> m >> k;
int u, v, w;
for (int i = 1; i <= n; i++) fa[i] = i;
for (int i = 0; i < m; i++) {
cin >> u >> v >> w;
if (w == 1) {
if (!is_in_same(u, v)) {
merge(u, v);
}
}
else {
enemy[u][v] = enemy[v][u] = 1;
}
}
for (int i = 0; i < k; i++) {
cin >> u >> v;
if (enemy[u][v] == 1) {
if (is_in_same(u, v)) cout << "OK but..." << endl;
else cout << "No way" << endl;
}
else {
if (is_in_same(u, v)) cout << "No problem" << endl;
else cout << "OK" << endl;
}
}
return 0;
}
L2-2 名人堂与代金券(C++,模拟)
对于在中国大学 M O O C MOOC MOOC(http://www.icourse163.org/ )学习“数据结构”课程的学生,想要获得一张合格证书,总评成绩必须达到 60 60 60 分及以上,并且有另加福利:总评分在 [ G , 100 ] [G, 100] [G,100] 区间内者,可以得到 50 50 50 元 P A T PAT PAT 代金券;在 [ 60 , G ) [60, G) [60,G) 区间内者,可以得到 20 20 20 元 P A T PAT PAT代金券。全国考点通用,一年有效。同时任课老师还会把总评成绩前 K K K 名的学生列入课程“名人堂”。本题就请你编写程序,帮助老师列出名人堂的学生,并统计一共发出了面值多少元的 P A T PAT PAT 代金券。
输入格式:
输入在第一行给出 3 3 3 个整数,分别是 N N N(不超过 10000 10 000 10000 的正整数,为学生总数)、 G G G(在 ( 60 , 100 60,100 60,100) 区间内的整数,为题面中描述的代金券等级分界线)、 K K K(不超过 100 100 100 且不超过 N N N 的正整数,为进入名人堂的最低名次)。接下来 N N N 行,每行给出一位学生的账号(长度不超过 15 15 15位、不带空格的字符串)和总评成绩(区间 [ 0 , 100 ] [0, 100] [0,100] 内的整数),其间以空格分隔。题目保证没有重复的账号。
输出格式:
首先在一行中输出发出的 P A T PAT PAT 代金券的总面值。然后按总评成绩非升序输出进入名人堂的学生的名次、账号和成绩,其间以 1 1 1 个空格分隔。需要注意的是:成绩相同的学生享有并列的排名,排名并列时,按账号的字母序升序输出。
输入样例:
10 80 5
cy@zju.edu.cn 78
cy@pat-edu.com 87
1001@qq.com 65
uh-oh@163.com 96
test@126.com 39
anyone@qq.com 87
zoe@mit.edu 80
jack@ucla.edu 88
bob@cmu.edu 80
ken@163.com 70
输出样例:
360
1 uh-oh@163.com 96
2 jack@ucla.edu 88
3 anyone@qq.com 87
3 cy@pat-edu.com 87
5 bob@cmu.edu 80
5 zoe@mit.edu 80
解题思路:
emmm没啥好说的,直接模拟
数据处理就是输入名字和分数存储在学生数组中,然后重载运算符之后调用sort()
排序学生
之后的模拟唯一需要注意的地方就是,假设 k k k是 7 7 7,那么如果所有人都并列第 7 7 7,我们就要输出所有人,也就是说进入名人堂的可能多于 k k k个
#include <iostream>
#include <map>
#include <string>
#include <algorithm>
using namespace std;
const int max_n = 1e4;
struct student { string name; int grade; }stus[max_n + 1];
int main() {
int n, g, k;
cin >> n >> g >> k;
string str;
int grade;
for (int i = 1; i <= n; i++) {
cin >> str >> grade;
stus[i] = student{ str,grade };
}
sort(stus + 1, stus + 1 + n, [](student s1, student s2){
if (s1.grade > s2.grade) return true;//按数字降序
else if (s1.grade == s2.grade) return s1.name < s2.name;//按字母升序
else return false;
});
int idx = 1;
long long sum = 0;
while (idx <= n) {
if (stus[idx].grade >= g) sum += 50;
else if (stus[idx].grade >= 60) sum += 20;
else break;
idx++;
}
cout << sum << endl;
int last_grade = 0, last_idx = 0;//保存发生并列时的分数和排名
for (int i = 1; i <= n; i++) {
if (stus[i].grade == last_grade) cout << last_idx;
else {
if (i > k) {
break;
}
cout << i;
last_grade = stus[i].grade;
last_idx = i;
}
cout << ' ' << stus[i].name << ' ';
cout << stus[i].grade << endl;
}
return 0;
}
L2-3 包装机(C++,模拟)
一种自动包装机的结构如图 1 1 1 所示。首先机器中有 N N N 条轨道,放置了一些物品。轨道下面有一个筐。当某条轨道的按钮被按下时,活塞向左推动,将轨道尽头的一件物品推落筐中。当 0 0 0 号按钮被按下时,机械手将抓取筐顶部的一件物品,放到流水线上。图 2 2 2 显示了顺序按下按钮 3 3 3、 2 2 2、 3 3 3、 0 0 0、 1 1 1、 2 2 2、 0 0 0 后包装机的状态。
图 1 1 1 自动包装机的结构
图 2 2 2 顺序按下按钮 3 3 3、 2 2 2、 3 3 3、 0 0 0、 1 1 1、 2 2 2、 0 0 0 后包装机的状态
一种特殊情况是,因为筐的容量是有限的,当筐已经满了,但仍然有某条轨道的按钮被按下时,系统应强制启动 0 0 0 号键,先从筐里抓出一件物品,再将对应轨道的物品推落。此外,如果轨道已经空了,再按对应的按钮不会发生任何事;同样的,如果筐是空的,按 0 0 0 号按钮也不会发生任何事。
现给定一系列按钮操作,请你依次列出流水线上的物品。
输入格式:
输入第一行给出 3 3 3 个正整数 N N N( ≤ 100 ≤100 ≤100)、 M M M( ≤ 1000 ≤1000 ≤1000)和 S m a x S_{max} Smax( ≤ 100 ≤100 ≤100),分别为轨道的条数(于是轨道从 1 1 1 到 N N N 编号)、每条轨道初始放置的物品数量、以及筐的最大容量。随后 N N N 行,每行给出 M M M 个英文大写字母,表示每条轨道的初始物品摆放。
最后一行给出一系列数字,顺序对应被按下的按钮编号,直到 − 1 −1 −1 标志输入结束,这个数字不要处理。数字间以空格分隔。题目保证至少会取出一件物品放在流水线上。
输出格式:
在一行中顺序输出流水线上的物品,不得有任何空格。
输入样例:
3 4 4
GPLT
PATA
OMSA
3 2 3 0 1 2 0 2 2 0 -1
输出样例:
MATA
解题思路:
AC代码如下
#include <iostream>
#include <stack>
using namespace std;
const int max_n = 100;
const int max_m = 1000;
const int max_s = 100;
int n, m, s;
stack<char>stk;
char line[max_n + 1][max_m];
int lft[max_n + 1];
char ans[max_n * max_m];
int main() {
cin >> n >> m >> s;
for (int i = 1; i <= n; i++) {
for (int j = 0; j < m; j++) {
cin >> line[i][j];
}
}
int num = 0, idx = 0;
while (num != -1) {
cin >> num;
if (num == 0) {
if (stk.empty()) continue;
ans[idx++] = stk.top();
stk.pop();
}
else {
if (lft[num] == m) continue;
if (stk.size() == s) {
ans[idx++] = stk.top();
stk.pop();
}
stk.push(line[num][lft[num]++]);
}
}
for (int i = 0; i < idx; i++) cout << ans[i];
return 0;
}
L2-4 愿天下有情人都是失散多年的兄妹(C++,搜索)
呵呵。大家都知道五服以内不得通婚,即两个人最近的共同祖先如果在五代以内(即本人、父母、祖父母、曾祖父母、高祖父母)则不可通婚。本题就请你帮助一对有情人判断一下,他们究竟是否可以成婚?
输入格式:
输入第一行给出一个正整数N
(
2
≤
2 ≤
2≤ N
≤
1
0
4
≤10^4
≤104),随后N
行,每行按以下格式给出一个人的信息:
本人ID 性别 父亲ID 母亲ID
其中ID
是
5
5
5位数字,每人不同;性别M
代表男性、F
代表女性。如果某人的父亲或母亲已经不可考,则相应的ID
位置上标记为-1
。
接下来给出一个正整数K
,随后K
行,每行给出一对有情人的ID
,其间以空格分隔。
注意:题目保证两个人是同辈,每人只有一个性别,并且血缘关系网中没有乱伦或隔辈成婚的情况。
输出格式:
对每一对有情人,判断他们的关系是否可以通婚:如果两人是同性,输出Never Mind
;如果是异性并且关系出了五服,输出Yes
;如果异性关系未出五服,输出No
。
输入样例:
24
00001 M 01111 -1
00002 F 02222 03333
00003 M 02222 03333
00004 F 04444 03333
00005 M 04444 05555
00006 F 04444 05555
00007 F 06666 07777
00008 M 06666 07777
00009 M 00001 00002
00010 M 00003 00006
00011 F 00005 00007
00012 F 00008 08888
00013 F 00009 00011
00014 M 00010 09999
00015 M 00010 09999
00016 M 10000 00012
00017 F -1 00012
00018 F 11000 00013
00019 F 11100 00018
00020 F 00015 11110
00021 M 11100 00020
00022 M 00016 -1
00023 M 10012 00017
00024 M 00022 10013
9
00021 00024
00019 00024
00011 00012
00022 00018
00001 00004
00013 00016
00017 00015
00019 00021
00010 00011
输出样例:
Never Mind
Yes
Never Mind
No
Yes
No
Yes
No
No
解题思路:
题中给出的是一张有向无环图,采用链式前向星维护
想要找出是否可以成婚,我们就需要遍历两个人的五服之内的亲属
搜索第一个人时采用标记,记录五服之内的亲属
搜索第二个人的时候检查标记,查看是否有共同亲属
最后根据搜索结果输出即可
AC代码如下
#include <iostream>
#include <string.h>
using namespace std;
const int max_n = 1e4;
const int max_num = 1e6;
struct edge { int v, next; }edges[max_n * 2];
int tot = -1;
int head[max_num];
bool book[max_num];
char sexs[max_num];
int n, k;
void add_edge(int u, int v) {
edges[++tot] = { v, head[u] }; head[u] = tot;
}
void pre_dfs(int x, int level) {
//treminal
if (level == 6) return;
//main body
book[x] = true;
for (int i = head[x]; i != -1; i = edges[i].next) {
int v = edges[i].v;
pre_dfs(v, level + 1);
}
}
bool after_dfs(int x, int level) {
//terminal
if (level == 6) return false;
//main body
if (book[x] == true) return true;
for (int i = head[x]; i != -1; i = edges[i].next) {
int v = edges[i].v;
if (after_dfs(v, level + 1)) return true;
}
return false;
}
int main() {
cin >> n;
int own, fa, mo;
char sex;
memset(head, -1, sizeof(int) * max_num);
for (int i = 0; i < n; i++) {
cin >> own >> sex >> fa >> mo;
if (fa != -1) {
add_edge(own, fa);
sexs[fa] = 'M';
}
if (mo != -1) {
add_edge(own, mo);
sexs[mo] = 'F';
}
sexs[own] = sex;
}
cin >> k;
int u, v;
for (int i = 0; i < k; i++) {
cin >> u >> v;
if (sexs[u] == sexs[v]) {
cout << "Never Mind" << endl;
continue;
}
memset(book, false, sizeof(bool) * max_num);
pre_dfs(u, 1);
bool ret = after_dfs(v, 1);
if (!ret) cout << "Yes" << endl;
else cout << "No" << endl;
}
return 0;
}