NEUQACM双周赛(四)

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++,数学)

T.jpg

你的外星人朋友不认得地球上的加减乘除符号,但是会算阶乘 —— 正整数 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=dk110k1++d1101+d0(0di9,i=0,,k1),请编写程序统计每种不同的个位数字出现的次数。例如:给定 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++,字符串)

题的目标很简单,就是求两个正整数AB的和,其中AB都在区间 [ 1 , 1000 ] [1,1000] [1,1000]。稍微有点麻烦的是,输入并不保证是两个正整数。

输入格式:

输入在一行给出AB,其间以空格分开。问题是AB不一定是满足要求的正整数,有时候可能是超出范围的数字、负数、带小数点的实数、甚至是一堆乱码。

注意:我们把输入中出现的第 1 1 1个空格认为是AB的分隔。题目保证至少存在一个空格,并且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 1N×M105 0 ≤ Q ≤ 1000 0≤Q≤1000 0Q1000),表示地图为 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 1N编号;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.JPG

1 1 1 自动包装机的结构

图2.JPG

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;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WitheredSakura_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值