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

7-3 就不告诉你 (10 分)

题目:

做作业的时候,邻座的小盆友问你:“五乘以七等于多少?”你应该不失礼貌地围笑着告诉他:“五十三。”本题就要求你,对任何一对给定的正整数,倒着输出它们的乘积。

输入格式:
输入在第一行给出两个不超过 1000 的正整数 A 和 B,其间以空格分隔。

输出格式:
在一行中倒着输出 A 和 B 的乘积。

输入样例:

5 7

输出样例:

53

知识点

to_string()函数:将数字常量转换为字符串,返回值为转换完毕的字符串
reverse()函数:C++中的reverse()函数
C++ reverse函数的用法
stoi()函数:将 n 进制的字符串转化为十进制
C++中stoi函数

代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
int x, y;
cin >> x >> y;
string res = to_string(x*y);
reverse(res.begin(), res.end());
cout << stoi(res);
return 0;
}

7-4 一元多项式求导 (10 分)

注意点:考虑为0时的情况

7-5 统计一行文本的单词个数 (15 分)

题目

本题目要求编写程序统计一行字符中单词的个数。所谓“单词”是指连续不含空格的字符串,各单词之间用空格分隔,空格数可以是多个。

输入格式:
输入给出一行字符。

输出格式:
在一行中输出单词个数。

输入样例:

Let's go to room 209.

输出样例:

5

优化点

利用cin对空格的处理特点来分隔单词,直接按字符串处理进行计数

代码

#include<bits/stdc++.h>
using namespace std;
int main()
{
string s;
  int cnt = 0;
  while(cin >> s) cnt++;
cout << cnt;
return 0;
}

7-7 三人行 (20 分)

题目

题目来自PAT (Basic Level) Practice。 子曰:“三人行,必有我师焉。择其善者而从之,其不善者而改之。”

本题给定甲、乙、丙三个人的能力值关系为:甲的能力值确定是 2 位正整数;把甲的能力值的 2 个数字调换位置就是乙的能力值;甲乙两人能力差是丙的能力值的 X 倍;乙的能力值是丙的 Y 倍。请你指出谁比你强应“从之”,谁比你弱应“改之”。

输入格式:
输入在一行中给出三个数,依次为:M(你自己的能力值)、X 和 Y。三个数字均为不超过 1000 的正整数。

输出格式:
在一行中首先输出甲的能力值,随后依次输出甲、乙、丙三人与你的关系:如果其比你强,输出 Cong;平等则输出 Ping;比你弱则输出 Gai。其间以 1 个空格分隔,行首尾不得有多余空格。

注意:如果解不唯一,则以甲的最大解为准进行判断;如果解不存在,则输出 No Solution。

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

48 3 7

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

48 Ping Cong Gai

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

48 11 6

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

No Solution

知识点

==穷举方法!==样本数量不大,说明了是两位数,直接穷举所有两位数
由于题目要求“如果解不唯一,则以甲的最大解为准进行判断”,所以从99开始穷举

注意点

数据类型:丙的值可能为小数,所以要用到double

代码

#include<iostream>
#include<cmath>
using namespace std;
void print(int m, double x)
{
if(x>m) cout << " Cong";
else if(x==m) cout << " Ping";
else cout << " Gai";
}
int main()
{
int m, x, y;
cin >> m >> x >> y;
int i;
for(i=99; i>=10; i--)
{
int a = i;
int b = a%10*10+a/10;
double c = 1.0*abs(a-b)/x;
if(b/c == y)
{
cout << i;
print(m, a);
print(m, b);
print(m, c);
break;
}
}
if(i==9) cout << "No Solution";
return 0;
}

7-8 螺旋方阵 (20 分)

题目

所谓“螺旋方阵”,是指对任意给定的N,将1到N×N的数字从左上角第1个格子开始,按顺时针螺旋方向顺序填入N×N的方阵里。本题要求构造这样的螺旋方阵。

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

输出格式:
输出N×N的螺旋方阵。每行N个数字,每个数字占3位。

输入样例:

5

输出样例:

 1  2  3  4  5
 16 17 18 19  6
 15 24 25 20  7
 14 23 22 21  8
 13 12 11 10  9

思路

先找到共同的规律后再进行循环

== 把这个方阵看成是一个方框的组合。每次大循环生成一个方框,一个大循环包含四个小循环,生成方框
的四条线。
i表示本次方框的起始列下标,则: 本次方框的结束列下标为n-i+1
则:本次方框的起始行下标为i,结束行下标为n-i+1
因此,顺时针方向得到的四条线的下标组合是== : [i][i~n-i+1], [i+1~n-i+1][n-i+1], [n-i+1][n-i~i], [n-i~i+1][i]

代码

#include<bits/stdc++.h>
using namespace std;
int res[11][11];
int main()
{
int n;
cin >> n;
int t = 1;
int k;
for(int i=1; i<=(n+1)/2; i++)
{
for(k=i; k<=n-i+1; k++) res[i][k] = t++;
for(k=i+1; k<=n-i+1; k++) res[k][n-i+1] = t++;
for(k=n-i; k>=i; k--) res[n-i+1][k] = t++;
for(k=n-i; k>=i+1; k--) res[k][i] = t++;
}
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++)
  printf("%3d", res[i][j]);
cout << endl;
}
return 0;
}

7-10 社交集群 (25 分)

题目

当你在社交网络平台注册时,一般总是被要求填写你的个人兴趣爱好,以便找到具有相同兴趣爱好的潜在的朋友。一个“社交集群”是指部分兴趣爱好相同的人的集合。你需要找出所有的社交集群。

输入格式:
输入在第一行给出一个正整数 N(≤1000),为社交网络平台注册的所有用户的人数。于是这些人从 1 到 N 编号。随后 N 行,每行按以下格式给出一个人的兴趣爱好列表:


输出格式:
首先在一行中输出不同的社交集群的个数。随后第二行按非增序输出每个集群中的人数。数字间以一个空格分隔,行末不得有多余空格。

输入样例:

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

输出样例:

3
4 3 1

知识点

并查集【算法与数据结构】—— 并查集
思路讲解+leetcode例题

思路

其实就是并查集,只要有爱好相同的用户,都归在一个集合。
A和B虽然没有共同爱好,但如果他们跟C都有共同爱好(不管是否一样),那ABC都在同一个集合

代码(注意看注释)

#include<bits/stdc++.h>
using namespace std;
//每个人的不同爱好共用一个根节点,所以用un()合并
//而合并过程中,假如另一个人的爱好曾出现过,则与之前的树合并,共用一个根节点 
int pre[1000000];//并查集
int a[1000]; //a[用户编号] = 第一个爱好编号
int root(int x) {//求x的根节点 
	while(x!=pre[x]) x = pre[x];
	return x;
}
void un(int x, int y) {
	int px = root(x);
	int py = root(y);
	pre[px] = py;
}
int main() {
	for(int i=0; i<=1000000; i++) pre[i] = i;
	int n;
	cin >> n;
	for(int i=1; i<=n; i++) {
		int m;
		scanf("%d:", &m);
		int x;//此人的第一个爱好 
		cin >> x;
		a[i] = x;
		while(--m) {
			int y;
			cin >> y;
			un(x, y);//当前读入的与上一个合并  
			//同一个人的爱好合并,根节点相同的爱好合并
			x = y;
		}
	}
	map<int, int> mp;// mp[集合的根] = 这个集合的人数
	for(int i=1; i<=n; i++) {
		int tmp = root(a[i]);// 查看每个人第一个爱好的根节点即可知道此人所在的集合的根节点,从而计算各个集合的人数
		mp[tmp]++;
	}
	vector<int> res;
	for(auto x:mp)//把每个集合的人数放到res中
		res.push_back(x.second);
	sort(res.begin(), res.end(), greater<int>());//非增序排序
	//注意这里greater<int>后面有个括号 
	cout << res.size() << endl;
	for(int i=0; i<res.size(); i++) {
		if(i>0) cout << " ";
		cout << res[i];
	}
	return 0;
}

7-11 悄悄关注 (25 分)

题目

新浪微博上有个“悄悄关注”,一个用户悄悄关注的人,不出现在这个用户的关注列表上,但系统会推送其悄悄关注的人发表的微博给该用户。现在我们来做一回网络侦探,根据某人的关注列表和其对其他用户的点赞情况,扒出有可能被其悄悄关注的人。

输入格式:
输入首先在第一行给出某用户的关注列表,格式如下:

人数N 用户1 用户2 …… 用户N
其中N是不超过5000的正整数,每个用户i(i=1, …, N)是被其关注的用户的ID,是长度为4位的由数字和英文字母组成的字符串,各项间以空格分隔。

之后给出该用户点赞的信息:首先给出一个不超过10000的正整数M,随后M行,每行给出一个被其点赞的用户ID和对该用户的点赞次数(不超过1000),以空格分隔。注意:用户ID是一个用户的唯一身份标识。题目保证在关注列表中没有重复用户,在点赞信息中也没有重复用户。

输出格式:
我们认为被该用户点赞次数大于其点赞平均数、且不在其关注列表上的人,很可能是其悄悄关注的人。根据这个假设,请你按用户ID字母序的升序输出可能是其悄悄关注的人,每行1个ID。如果其实并没有这样的人,则输出“Bing Mei You”。

输入样例1:

10 GAO3 Magi Zha1 Sen1 Quan FaMK LSum Eins FatM LLao
8
Magi 50
Pota 30
LLao 3
Ammy 48
Dave 15
GAO3 31
Zoro 1
Cath 60

输出样例1:

Ammy
Cath
Pota

输入样例2:

11 GAO3 Magi Zha1 Sen1 Quan FaMK LSum Eins FatM LLao Pota
7
Magi 50
Pota 30
LLao 48
Ammy 3
Dave 15
GAO3 31
Zoro 29

输出样例2:

Bing Mei You

知识点

  • 若需要使用set或map的特性而不希望它进行排序,可以使用unordered_set<数据类型> 或unordered_map<>
    C++ STL unordered_set容器完全攻略
  • 运用迭代器时指向容器的first或second时,要用 ->
  • 在这里插入图片描述
  • 判断某个值是否在容器中方法:
    – 使用find()函数,看是否==容器.end(),若等于说明没找到
    在这里插入图片描述

– 使用count()函数,若容器.count()值为0,则说明不存在
在这里插入图片描述

代码

#include<bits/stdc++.h>
using namespace std;
int main()
{
  int n;
cin >> n;
unordered_set<string> s;
for(int i=0; i<n; i++)
{
string t;
cin >> t;
s.insert(t);
}
cin >> n;
map<string, int> mp;
int sum = 0;
for(int i=0; i<n; i++)
{
string t;
int x;
cin >> t >> x;
mp[t] = x;
sum += x;
}
double avg = sum/n;
int flag = 1;
for(auto it=mp.begin(); it!=mp.end(); it++)
if(it->second>avg && s.find(it->first)==s.end())
{
cout << it->first << endl;
flag = 0;
}
if(flag) cout << "Bing Mei You";
return 0;
}

7-13 大勾股定理(仿2021 520钻石争霸赛7-5—浙江大学 陈越) (25 分)

题目

大勾股定理是勾股定理的推广:对任何正整数 n 存在 2n+1 个连续正整数,满足前 n+1 个数的平方和等于后 n 个数的平方和。例如对于 n=1有32+42= 52 ;n=2 有102 +112+122 =132+142等。给定 n,本题就请你找出对应的解。

输入格式:
输入在一行中给出正整数 n(≤104 )。

输出格式:
分两行输出满足大勾股定理的解,格式如下:

a[0]^2 + a[1]^2 ++ a[n]^2 =

a[n+1]^2 ++ a[2n]^2

其中解的数列 a[0] … a[2n] 按递增序输出。注意行首尾不得有多余空格。

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

3

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

21^2 + 22^2 + 23^2 + 24^2 =
25^2 + 26^2 + 27^2

提示:

50%的数据:n <= 500

83%的数据:n <= 1000

100%的数据:n <= 10000

思路

这题目,如果使用滑动窗口方法(就是滑动起点终点来计算),最后一个点超时
如果你没有超时而是答案错误,那就是你类型都不对,不能int只能long long
当n为10000时,正确的第2n+1个数是 200030000, 所以光是这个数的平方也得long long
如果使用==数学解析==的方法,绝对不超

直接根据方程式求解出根,直接进行运算

代码

#include<bits/stdc++.h>
using namespace std;
int main()
{
int n;
cin >> n;
int x = 2*n*n+n;
int i = 0;
for(; i<=n; i++)
{
if(i>0) cout << " + ";
cout << x+i << "^2";
}
cout << " =\n";
for(; i<2*n+1; i++)
{
if(i>n+1) cout << " + ";
cout << x+i << "^2";
}
return 0;
}

7-9 网红点打卡攻略 (25 分)

题目

一个旅游景点,如果被带火了的话,就被称为“网红点”。大家来网红点游玩,俗称“打卡”。在各个网红点打卡的快(省)乐(钱)方法称为“攻略”。你的任务就是从一大堆攻略中,找出那个能在每个网红点打卡仅一次、并且路上花费最少的攻略。

输入格式:
首先第一行给出两个正整数:网红点的个数 N(1<N≤200)和网红点之间通路的条数 M。随后 M 行,每行给出有通路的两个网红点、以及这条路上的旅行花费(为正整数),格式为“网红点1 网红点2 费用”,其中网红点从 1 到 N 编号;同时也给出你家到某些网红点的花费,格式相同,其中你家的编号固定为 0。

再下一行给出一个正整数 K,是待检验的攻略的数量。随后 K 行,每行给出一条待检攻略,格式为:
在这里插入图片描述
输出格式:
在第一行输出满足要求的攻略的个数。

在第二行中,首先输出那个能在每个网红点打卡仅一次、并且路上花费最少的攻略的序号(从 1 开始),然后输出这个攻略的总路费,其间以一个空格分隔。如果这样的攻略不唯一,则输出序号最小的那个。

题目保证至少存在一个有效攻略,并且总路费不超过 10
9

输入样例:

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

输出样例:

3
5 11

样例说明:
第 2、3、4、6 条都不满足攻略的基本要求,即不能做到从家里出发,在每个网红点打卡仅一次,且能回到家里。所以满足条件的攻略有 3 条。

第 1 条攻略的总路费是:(0->5) 2 + (5->1) 2 + (1->4) 2 + (4->3) 2 + (3->6) 2 + (6->2) 2 + (2->0) 2 = 14;

第 5 条攻略的总路费同理可算得:1 + 1 + 1 + 2 + 3 + 1 + 2 = 11,是一条更省钱的攻略;

第 7 条攻略的总路费同理可算得:2 + 1 + 1 + 2 + 2 + 2 + 1 = 11,与第 5 条花费相同,但序号较大,所以不输出。

思路

  • 关键在于检查攻略的合法性,根本不需要什么dfs或最短路径
  • 家的编号为0
  • 攻略无效的情况:1-攻略中没有包含所有网红点(1~n); 2-攻略中某两点没有边 3-最后一个网红点不能回家

知识点

通过判断各个攻略中的网红点数和set集合的大小来判断该攻略是否满足经过了每个网红点且只经过一次这个条件

代码

#include<bits/stdc++.h>
using namespace std;
int main()
{
int n, m;
cin >> n >> m;
int a[201][201] = {0};
while(m--)
{
int x, y, z;
cin >> x >> y >> z;
a[x][y] = a[y][x] = z;
}
cin >> m;
int cnt = 0;
int min = 1e+9+1, minid;
for(int i=1; i<=m; i++)
{
int k;
cin >> k;
int tmp[201] = {0};
set<int> s;//使用set和p的大小是否与n相同来判断此攻略是否在每个网红点打卡仅一次 
int flag = 0;//判断攻略中是否存在不能互相到达的相邻点 
int sum = 0;
for(int j=1; j<=k; j++)
{
cin >> tmp[j];
s.insert(tmp[j]);
if(a[tmp[j-1]][tmp[j]]==0) flag = 1;
sum += a[tmp[j-1]][tmp[j]];
}
if(k!=n || s.size()!=n || flag) continue;
if(a[tmp[k]][0]==0) continue;//最后一个点没有边回家
cnt++;
sum += a[tmp[k]][0];
if(sum < min) min = sum, minid = i;
}
cout << cnt << endl;
cout << minid << " " << min;
}

7-12 六度空间 (25 分)

题目

“六度空间”理论又称作“六度分隔(Six Degrees of Separation)”理论。这个理论可以通俗地阐述为:“你和任何一个陌生人之间所间隔的人不会超过六个,也就是说,最多通过五个人你就能够认识任何一个陌生人。”如图1所示。

图1 六度空间示意图
“六度空间”理论虽然得到广泛的认同,并且正在得到越来越多的应用。但是数十年来,试图验证这个理论始终是许多社会学家努力追求的目标。然而由于历史的原因,这样的研究具有太大的局限性和困难。随着当代人的联络主要依赖于电话、短信、微信以及因特网上即时通信等工具,能够体现社交网络关系的一手数据已经逐渐使得“六度空间”理论的验证成为可能。

假如给你一个社交网络图,请你对每个节点计算符合“六度空间”理论的结点占结点总数的百分比。

输入格式:
输入第1行给出两个正整数,分别表示社交网络图的结点数N(1<N≤10
3
,表示人数)、边数M(≤33×N,表示社交关系数)。随后的M行对应M条边,每行给出一对正整数,分别是该条边直接连通的两个结点的编号(节点从1到N编号)。

输出格式:
对每个结点输出与该结点距离不超过6的结点数占结点总数的百分比,精确到小数点后2位。每个结节点输出一行,格式为“结点编号:(空格)百分比%”。

输入样例:

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

输出样例:

1: 70.00%
2: 80.00%
3: 90.00%
4: 100.00%
5: 100.00%
6: 100.00%
7: 100.00%
8: 90.00%
9: 80.00%
10: 70.00%

知识点

  • ==弗洛伊德(Floyd算法)==求任意两点间的最短距离
    算法模板:
void Floyd()
{//求任两点的最短距离,保存在a数组
	for(int k=1; k<=n; k++)
		for(int i=1; i<=n; i++)
			for(int j=1; j<=n; j++)
				a[i][j] = min(a[i][j], a[i][k]+a[k][j]);
}

k为“中间点”,遍历所有点依次作为中间点,若i,j两点间距离a[i][j]>a[i][k]+a[k][j],则将a[i][j]更新为a[i][k]+a[k][j]
参考链接:Floyd算法详解 通俗易懂
最短路径Floyd算法

注意点

代码

#include <bits/stdc++.h>
using namespace std;
int n,m;
int a[1005][1005];//初始是存放直接连通的连通的两条边的距离(距离为1)
void floyd(){
//求任两点的最短距离,保存在a数组
	for(int k=1;k<=n;k++){//中间点 
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				a[i][j] = min(a[i][j],a[i][k]+a[k][j]);
			}
		}
	}
}
int main(){
	cin>>n>>m;
//	memset(a,0,sizeof(a));//!!!!!
	for(int i=1; i<=n; i++)
		for(int j=1; j<=n; j++)
			a[i][j] = 1100;
//	for(int i=1; i<=n; i++){
//		for(int j=1; j<=n; j++){
//			cout<<a[i][j]<<" ";
//		}
//		cout<<endl;
//	}
		
	for(int i=0;i<m;i++){
		int x,y;
		cin>>x>>y;
		a[x][y] = a[y][x] = 1;
	}
	floyd();
	for(int i=1;i<=n;i++){
		int cnt = 0;
		for(int j=1;j<=n;j++){
			if(a[i][j]<=6) cnt++;
		}
		printf("%d: %.2lf%%\n",i,100.0*cnt/n);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

GCTTTTTT

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

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

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

打赏作者

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

抵扣说明:

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

余额充值