团队程序设计天梯赛-4.9排位赛总结

7-1 谁是赢家 (5 分)

题目描述

某电视台的娱乐节目有个表演评审环节,每次安排两位艺人表演,他们的胜负由观众投票和 3 名评委投票两部分共同决定。规则为:如果一位艺人的观众票数高,且得到至少 1 名评委的认可,该艺人就胜出;或艺人的观众票数低,但得到全部评委的认可,也可以胜出。节目保证投票的观众人数为奇数,所以不存在平票的情况。本题就请你用程序判断谁是赢家。

输入格式:
输入第一行给出 2 个不超过 1000 的正整数 Pa 和 Pb,分别是艺人 a 和艺人 b 得到的观众票数。题目保证这两个数字不相等。随后第二行给出 3 名评委的投票结果。数字 0 代表投票给 a,数字 1 代表投票给 b,其间以一个空格分隔。

输出格式:
按以下格式输出赢家:

The winner is x: P1 + P2
其中 x 是代表赢家的字母,P1 是赢家得到的观众票数,P2 是赢家得到的评委票数。

输入样例:

327 129
1 0 1

输出样例:

The winner is a: 327 + 1

代码

#include<bits/stdc++.h>
using namespace std;
int main() {
	int a, b;
	cin >> a >> b;
	int x, y, z;
	cin >> x >> y >> z;
	int res = x+y+z;
	if(a>b && res<3 || a<b && res==0)
		printf("The winner is a: %d + %d", a, 3-res);
	else
		printf("The winner is b: %d + %d", b, res);
}

7-2 有理数比较 (5 分)

题目描述

本题要求编写程序,比较两个有理数的大小。

输入格式:
输入在一行中按照“a1/b1 a2/b2”的格式给出两个分数形式的有理数,其中分子和分母全是整形范围内的正整数。

输出格式:
在一行中按照“a1/b1 关系符 a2/b2”的格式输出两个有理数的关系。其中“>”表示“大于”,“<”表示“小于”,“=”表示“等于”。

输入样例1:

1/2 3/4

输出样例1:

1/2 < 3/4

输入样例2:

6/8 3/4

输出样例2:

6/8 = 3/4

注意点

分数运算,尽量转为乘法,因为除法会失真。

代码

#include<bits/stdc++.h>
using namespace std;
int main() {
	int a, b, c, d;
	scanf("%d/%d %d/%d", &a, &b, &c, &d);
	if(a*d > b*c) printf("%d/%d > %d/%d", a, b, c, d);
	else if(a*d == b*c) printf("%d/%d = %d/%d", a, b, c, d);
	else printf("%d/%d < %d/%d", a, b, c, d);
}

7-3 猴子选大王 (15 分)

题目描述

一群猴子要选新猴王。新猴王的选择方法是:让N只候选猴子围成一圈,从某位置起顺序编号为1~N号。从第1号开始报数,每轮从1报到3,凡报到3的猴子即退出圈子,接着又从紧邻的下一只猴子开始同样的报数。如此不断循环,最后剩下的一只猴子就选为猴王。请问是原来第几号猴子当选猴王?

输入格式:
输入在一行中给一个正整数N(≤1000)。

输出格式:
在一行中输出当选猴王的编号。

输入样例:

11

输出样例:

7

代码思路

使用队列queue,如果是三的倍数则跳过,否则将队头push到队尾

代码

#include<bits/stdc++.h>
using namespace std;
int main() {
	int n;
	cin >> n;
	queue<int> q;
	for(int i=1; i<=n; i++) q.push(i);
	int cnt = 1;
	while(q.size()>1) {
		int x = q.front();
		q.pop();
		if(cnt%3!=0) q.push(x);
		cnt++;
	}
	cout << q.front() << endl;
}

7-4 A减B (15 分)

题目描述

给定两个正整数 A≥B,我们在计算 A−B 的时候,从个位开始依次对每一位做减法。当 A 的某位数字 a 比 B 的对应位数字 b 小的时候,A 就需要向高位借 10,然后计算 10+a−b。

本题就请你写程序统计一次减法中到底做了多少次借位操作。

输入格式:
输入在两行中先后给出两个正整数 A 和 B,满足 0 < B ≤ A ≤ 10
10000

输出格式:
在一行中输出计算 A−B 过程中借位操作的次数。

输入样例:

92338249361576354802
88915368421673890711

输出样例:

10

注意点

  • 输入的数可能很大。所以使用string进行按位处理
  • 注意A>B所以有可能B的位数小于A的位数,若B位数小于A的话要在B前面补0:
 t = string(s.size()-t.size(), '0') + t;

代码

#include<bits/stdc++.h>
using namespace std;
int main() {
	string s, t;
	cin >> s >> t;
	t = string(s.size()-t.size(), '0') + t;
	int res = 0;
	int flag = 0;
	for(int i = s.size()-1; i>=0; i--) {
		if(s[i]-flag < t[i]) res++, flag = 1;
		else flag = 0;

	}
	cout << res;
	return 0;
}

7-5 连续因子 (20 分)

题目描述

一个正整数 N 的因子中可能存在若干连续的数字。例如 630 可以分解为 3×5×6×7,其中 5、6、7 就是 3 个连续的数字。给定任一正整数 N,要求编写程序求出最长连续因子的个数,并输出最小的连续因子序列。

输入格式:
输入在一行中给出一个正整数 N(1<N<2
31
)。

输出格式:
首先在第 1 行输出最长连续因子的个数;然后在第 2 行中按 因子1因子2……*因子k 的格式输出最小的连续因子序列,其中因子按递增顺序输出,1 不算在内。

输入样例:

630

输出样例:

3
5*6*7

思路

就是一个个去试:
先以2开头,2行就试试2-3,2-3行就试试2-3-4,…
再以3开头,…
注意特别处理素数的情况

代码

#include<cmath>
#include<iostream>
using namespace std;
int main() {
	int n;
	cin >> n;
	int i,j;
	int mmax = 0, end;
	for(i=2; i<=sqrt(n); i++) { //测到sqrt(n)就行,因为后面的肯定不是因子
		int num = n;
		int j;
		for(j=i; j<=sqrt(n)+1 && num%j==0; num/=j, j++);
		if(j-i>mmax) { //本次的连续因子长度更大
			mmax = j-i;
			end = j-1;
		}
	}
	if(mmax==0) cout << 1 << endl << n;//素数的情况
	else {
		cout << mmax << endl;
		for(i=end-mmax+1; i<end; i++)
			cout << i << "*";
		cout << end;
	}
	return 0;
}

7-6 接发球 (20 分)

题目描述

有n个篮球,它们的编号是1~n。我们按照编号顺序给你发球。你每接到一个球,你能做两个事情:要么把手上的这个球发出去,要么把球放入到球框(如果还放得下的话)。如果你现在手上没有球,你可以选择接下一个球或者从球筐里取出一个球发出去。
现按照编号指定一个发球顺序,请你帮忙完成任务。
注意:我们只提供一个球筐,这个球框最多只能放m个球,框开口向上,每层只能放一个球。所以取球时只能从上往下顺序取。
好了,请你告诉我能完成任务吗,能的话,输出YES,否则,输出NO。

输入格式:
第一行有三个整数,n, m, k(0<=n<=100, 0<m<=100),分别代表篮球数量,球框容量,以及有多少次询问。
接下来有k行,每行是n个数,代表我们指定的一个编号排列,这个排列就是我们要求的发球顺序。

输出格式:
输出有k行,每行输出对应排列是否能完成。能的话,输出YES,否则,输出NO.

输入样例:

6 3 2
3 2 1 4 6 5
1 6 5 4 3 2 

输出样例:

YES
NO

结果说明:
1、对于3 2 1 4 6 5,可以接1放框里,接2放框里,接到的3发出去,再把框里的2和1发出去,然后接4,发出去,再接6放框里,接5发出去,再把框里的6发出去。
2、对于1 6 5 4 3 2,可以接1发出去,然后三次接球放框,再接到的5已经放不下了,所以无法完成任务。

代码

#include<bits/stdc++.h>
using namespace std;
int n, m, k;
bool ju(int a[]) {
	stack<int> s;
	int now = 1;
	int flag = 1;
	int i = 0;
	while(i<n && flag) {
		if(now == a[i]) {
			now++;
			i++;
		} else if(now<a[i]) { //此时要入栈
			while(now<a[i] && s.size()<m) s.push(now++);
			if(now<a[i]) flag = 0;//说明栈满了
			else i++, now++;
		} else { //此时要出栈
			if(s.top()!=a[i]) flag = 0;
			else {
				s.pop();
				i++;
			}
		}
	}
	if(!flag) return false;
	while(!s.empty()) { //还得比较出栈序列是否正确
		if(s.top()!=a[i]) return false;
		s.pop();
		i++;
	}
	return true;
}
int main() {
	cin >> n >> m >> k;
	int a[101];
	for(int i=0; i<k; i++) {
		for(int j=0; j<n; j++) cin >> a[j];
		if(ju(a)) cout << "YES\n";
		else cout << "NO\n";
	}
}

7-7 哈利·波特的考试 (25 分)

题目描述

哈利·波特要考试了,他需要你的帮助。这门课学的是用魔咒将一种动物变成另一种动物的本事。例如将猫变成老鼠的魔咒是haha,将老鼠变成鱼的魔咒是hehe等等。反方向变化的魔咒就是简单地将原来的魔咒倒过来念,例如ahah可以将老鼠变成猫。另外,如果想把猫变成鱼,可以通过念一个直接魔咒lalala,也可以将猫变老鼠、老鼠变鱼的魔咒连起来念:hahahehe。

现在哈利·波特的手里有一本教材,里面列出了所有的变形魔咒和能变的动物。老师允许他自己带一只动物去考场,要考察他把这只动物变成任意一只指定动物的本事。于是他来问你:带什么动物去可以让最难变的那种动物(即该动物变为哈利·波特自己带去的动物所需要的魔咒最长)需要的魔咒最短?例如:如果只有猫、鼠、鱼,则显然哈利·波特应该带鼠去,因为鼠变成另外两种动物都只需要念4个字符;而如果带猫去,则至少需要念6个字符才能把猫变成鱼;同理,带鱼去也不是最好的选择。

输入格式:
输入说明:输入第1行给出两个正整数N (≤100)和M,其中N是考试涉及的动物总数,M是用于直接变形的魔咒条数。为简单起见,我们将动物按1~N编号。随后M行,每行给出了3个正整数,分别是两种动物的编号、以及它们之间变形需要的魔咒的长度(≤100),数字之间用空格分隔。

输出格式:
输出哈利·波特应该带去考场的动物的编号、以及最长的变形魔咒的长度,中间以空格分隔。如果只带1只动物是不可能完成所有变形要求的,则输出0。如果有若干只动物都可以备选,则输出编号最小的那只。

输入样例:

6 11
3 4 70
1 2 1
5 4 50
2 6 50
5 6 60
1 3 70
4 6 60
3 6 80
5 1 100
2 4 60
5 2 80

输出样例:

4 70

思路

使用Floyd算法算出每个点到另一个点的最短路径,求出各个i到每个j的距离最大值的最小值即为对应解

*知识点

Floyd算法模板:
注意是k—i—j

int n;
int a[101][101];
void Floyd() {
	for(int k=1; k<=n; k++)
		for(int i=1; i<=n; i++)
			for(int j=1; j<=n; j++)
				if(a[i][k]+a[k][j]<a[i][j]) a[i][j] = a[i][k]+a[k][j];
}

代码

#include<bits/stdc++.h>
using namespace std;
const int MAX = 999999;
int n;
int a[101][101];
void Floyd() {
	for(int k=1; k<=n; k++)
		for(int i=1; i<=n; i++)
			for(int j=1; j<=n; j++)
				if(a[i][k]+a[k][j]<a[i][j]) a[i][j] = a[i][k]+a[k][j];
}
int main() {
	int m;
	cin >> n >> m;
	for(int i=1; i<=n; i++)
		for(int j=1; j<=n; j++)
			if(i==j) a[i][j] = 0;
			else a[i][j] = MAX;
	while(m--) {
		int x, y, z;
		cin >> x >> y >> z;
		a[x][y] = a[y][x] = z;
	}
	Floyd();
	int res = MAX;
	int flag = 1;
	int num;
	for(int i=1; i<=n && flag; i++) {
		int tmpmax = 0;
		for(int j=1; j<=n; j++)
			if(a[i][j]>tmpmax) tmpmax = a[i][j];//i变成其他动物的最长长度
		if(tmpmax == MAX) flag = 0;
		else if(tmpmax < res) { //更短的最长长度
			res = tmpmax;
			num = i;
		}
	}
	if(flag) cout << num << " " << res << endl;
	else cout << 0 << endl;
}

7-8 快速排序 (25 分)

题目描述

著名的快速排序算法里有一个经典的划分过程:我们通常采用某种方法取一个元素作为主元,通过交换,把比主元小的元素放到它的左边,比主元大的元素放到它的右边。 给定划分后的 N 个互不相同的正整数的排列,请问有多少个元素可能是划分前选取的主元?

例如给定 N=5, 排列是1、3、2、4、5。则:

1 的左边没有元素,右边的元素都比它大,所以它可能是主元;
尽管 3 的左边元素都比它小,但其右边的 2 比它小,所以它不能是主元;
尽管 2 的右边元素都比它大,但其左边的 3 比它大,所以它不能是主元;
类似原因,4 和 5 都可能是主元。
因此,有 3 个元素可能是主元。

输入格式:
输入在第 1 行中给出一个正整数 N(≤10
5
); 第 2 行是空格分隔的 N 个不同的正整数,每个数不超过10
9

输出格式:
在第 1 行中输出有可能是主元的元素个数;在第 2 行中按递增顺序输出这些元素,其间以 1 个空格分隔,行首尾不得有多余空格。

输入样例:

5
1 3 2 4 5

输出样例:

3
1 4 5

*思路

  • 每一次划分后,主元的位置必定就是排序后的位置
  • 利用这个特点,把输入数据排序得到b[],然后与原数据a[]按照位置一一对比
  • 如果是主元,必定满足两个条件:b[i]=a[i]a[i]大于a[1:i-1]
  • 遍历时不断更新该位置前的最大值从而进行a[i]和a[1:i-1]的比较

代码

//注意个数为0时要输出一个空行,否则格式错误。
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
int main() {
	int n;
	cin >> n;
	int a[100000], b[100000], res[100000];
	for(int i=0; i<n; i++) {
		cin >> a[i];
		b[i] = a[i];
	}
	sort(b, b+n);
	int cnt = 0;
	int max = 0;
	for(int i=0; i<n; i++) {
		if(a[i]==b[i] && a[i]>max) res[cnt++] = a[i];
		if(a[i]>max) max = a[i];
	}
	cout << cnt << endl;
	for(int i=0; i<cnt; i++) {
		if(i>0) cout << " ";
		cout << res[i];
	}
	return 0;
}

7-9 喊山 (25 分)todo

题目描述

喊山,是人双手围在嘴边成喇叭状,对着远方高山发出“喂—喂喂—喂喂喂……”的呼唤。呼唤声通过空气的传递,回荡于深谷之间,传送到人们耳中,发出约定俗成的“讯号”,达到声讯传递交流的目的。原来它是彝族先民用来求援呼救的“讯号”,慢慢地人们在生活实践中发现了它的实用价值,便把它作为一种交流工具世代传袭使用。
一个山头呼喊的声音可以被临近的山头同时听到。题目假设每个山头最多有两个能听到它的临近山头。给定任意一个发出原始信号的山头,本题请你找出这个信号最远能传达到的地方。

输入格式:
输入第一行给出3个正整数n、m和k,其中n(≤10000)是总的山头数(于是假设每个山头从1到n编号)。接下来的m行,每行给出2个不超过n的正整数,数字间用空格分开,分别代表可以听到彼此的两个山头的编号。这里保证每一对山头只被输入一次,不会有重复的关系输入。最后一行给出k(≤10)个不超过n的正整数,数字间用空格分开,代表需要查询的山头的编号。

输出格式:
依次对于输入中的每个被查询的山头,在一行中输出其发出的呼喊能够连锁传达到的最远的那个山头。注意:被输出的首先必须是被查询的个山头能连锁传到的。若这样的山头不只一个,则输出编号最小的那个。若此山头的呼喊无法传到任何其他山头,则输出0。

输入样例:

7 5 4
1 2
2 3
3 1
4 5
5 6
1 4 5 7

输出样例:

2
6
4
0

*知识点(todo)

广搜

代码

#include<bits/stdc++.h>
using namespace std;
const int mm = 10001;
int m, n, k;
vector<int> a[mm];
int id, bcnt;
void bfs(int t) {
	//广搜
	queue<pair<int, int>> q;
	q.push({t, 0});//{顶点号,该顶点层数}
	bool vis[mm] = {0};
	while(!q.empty()) {
		auto x = q.front();
		q.pop();
		if(vis[x.first]) continue;
		vis[x.first] = 1;
		if(bcnt<x.second) bcnt = x.second, id = x.first;
		else if(bcnt==x.second) id = min(id, x.first);
		for(auto tmp:a[x.first])
			if(!vis[tmp]) q.push({tmp, x.second+1});
	}
}
int main() {
	cin >> n >> m >> k;
	while(m--) {
		int x, y;
		cin >> x >> y;
		a[x].push_back(y);
		a[y].push_back(x);
	}
	while(k--) {
		int x;
		cin >> x;
		bcnt = 0;
		id = 0;
		bfs(x);
		cout << id << endl;
	}
}

7-10 千年一遇对称日 (25 分)

题目描述

在这里插入图片描述
在 2021 年 12 月 2 日这天,有人发了这么一张图,上书“千年一遇对称日,万事圆满在今朝”。话是挺吉利的,但好像下一个对称日并不需要等上一千年…… 下面任给一个人的生卒年份,请你列出来这个人一辈子经过的所有对称日。

输入格式:
输入第一行给出一个人的名字,是由不超过30个字符的英文字母和空格组成的、以回车结束的非空字符串。第二行按 yyyymmdd - yyyymmdd 的格式给出这个人的生卒年(yyyy)、月(mm)、日(dd)。

虽然生卒日期可能是虚构的,但题目保证出生日期在去世日期之前,且每个日期都是合法的,从 00010101 到 99991231。注意这里遵循“四年一闰,百年不闰,四百年再闰”的规则定义闰年,即年份是 4 的倍数,且不是 100 的倍数的,为普通闰年;年份是整百数的,必须是 400 的倍数才是闰年。闰年跟普通年的区别就是 2 月有 29 天。

输出格式:
首先在第一行原样输出这个人的名字。随后按照时间顺序,每行输出该人所经历的一个对称日,格式与输入格式相同。注意:生卒日期也包含在内。

输入样例:

Katherine Johnson
19180826 - 20200224

输出样例:

Katherine Johnson
20011002
20100102
20111102
20200202

思路

利用月和日反过来等于年份来判断是否对称
注意0001等有前导0的年份对0的处理

代码

#include<bits/stdc++.h>
using namespace std;
bool leap(int y) {
	if ((y % 4 == 0 && y % 100 != 0) || y % 400 == 0) return 1;
	else return 0;
}
int main() {
	int mon[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
	string na;
	getline(cin, na);
	cout << na << endl;
	string s, t;
	cin >> s >> t >> t;
	int y = stoi(s.substr(0, 4));
	int m = stoi(s.substr(4, 2));
	int d = stoi(s.substr(6, 2));
	int end = stoi(t);
	while (y * 10000 + m * 100 + d <= end) {
		int x = d % 10 * 10 + d / 10;
		x = x * 100 + m % 10 * 10 + m / 10;
		if (x == y) printf("%04d%02d%02d\n", y, m, d);
		if (m==2 && d==28 && leap(y)) d = 29;
		else {
			if (d < mon[m]) d++;
			else {
				d = 1;
				if (m == 12) m = 1, y++;
				else m++;
			}
		}
	}
	return 0;
}

7-11 浪漫侧影(25 分)todo

题目描述

在这里插入图片描述
“侧影”就是从左侧或者右侧去观察物体所看到的内容。例如上图中男生的侧影是从他右侧看过去的样子,叫“右视图”;女生的侧影是从她左侧看过去的样子,叫“左视图”。
520 这个日子还在打比赛的你,也就抱着一棵二叉树左看看右看看了……
我们将二叉树的“侧影”定义为从一侧能看到的所有结点从上到下形成的序列。例如下图这棵二叉树,其右视图就是 { 1, 2, 3, 4, 5 },左视图就是 { 1, 6, 7, 8, 5 }。
在这里插入图片描述
于是让我们首先通过一棵二叉树的中序遍历序列和后序遍历序列构建出一棵树,然后你要输出这棵树的左视图和右视图。

输入格式:
输入第一行给出一个正整数 N (≤20),为树中的结点个数。随后在两行中先后给出树的中序遍历和后序遍历序列。树中所有键值都不相同,其数值大小无关紧要,都不超过 int 的范围。

输出格式:
第一行输出右视图,第二行输出左视图,格式如样例所示。

输入样例:
在这里给出一组输入。例如:

8
6 8 7 4 5 1 3 2
8 5 4 7 6 3 2 1

输出样例:
在这里给出相应的输出。例如:

R: 1 2 3 4 5
L: 1 6 7 8 5

思路

  • 还是根据两个顺序来获得层次遍历的结果。
  • 利用每个结点的唯一编号来获得每个层次的第一个结点和最后一个结点。

代码

#include<bits/stdc++.h>
using namespace std;
int a[20], b[20];//中序, 后序
vector<pair<int, int>> res[22];//res[层次] = {该层次的 结点编号和值 序列:{编号, 值},...}}
void tree(int al, int ar, int bl, int br, int level, int num) {
	//处理a[al:ar]与b[bl:br] 对应的子树,并记录本子树的根的层次和值和编号
	if(al>ar) return;//子树不存在
	res[level].push_back({num, b[br]});
//cout << level << " " << num << " " << b[br] << endl;
	int i;
	for(i=al; a[i]!=b[br]; i++);//在a中找本子树的根
	tree(al, i-1, bl, bl+(i-1-al), level+1, num*2);//处理左子树
	tree(i+1, ar, bl+i-al, br-1, level+1, num*2+1);//处理右子树
}
int main() {
	int n;
	cin >> n;
	for(int i=0; i<n; i++) cin >> a[i];
	for(int i=0; i<n; i++) cin >> b[i];
	tree(0, n-1, 0, n-1, 1, 1);
	cout << "R:";
	for(int i=1; res[i].size()>0; i++) {
		sort(res[i].begin(), res[i].end());
		cout << " " << res[i].back().second;
	}
	cout << endl << "L:";
	for(int i=1; res[i].size()>0; i++)
		cout << " " << res[i].front().second;
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

GCTTTTTT

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

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

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

打赏作者

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

抵扣说明:

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

余额充值