新生个人训练赛第6场题解

新生个人训练赛第6场题解

1. Problem A 石头剪刀布

题目描述

石头剪刀布是常见的猜拳游戏,规则是:
小A和小B两人双方同时各出石头剪刀布中的一个,石头赢剪刀,剪刀赢布,布赢石头。现在小A和小B双方进行比赛,根据各自出的拳进行判断输赢。
为了表示方便,现规定:
0代表石头;1代表剪刀;2代表布。
如:A出0,B出1,则本轮A赢。
请问,小A和小B比赛了5轮之后,谁赢的轮数多?

输入

5行,每行两个用一个空格隔开的x和y,x代表A出的拳,y代表B出的拳。x和y是一定是0 、1 、2中的一个。

输出

如果A方赢的轮数多,输出:A
如果B方赢的轮数多,输出:B
如果双方赢的轮数一样,输出:D

样例输入

0 1
1 0
1 2
2 0
0 0

样例输出

A

解题思路

观察发现,赢的情况减去输的情况要不是-1,要不是2,依据此结论统计A、B赢得场数,并输出即可

#include<iostream>
using namespace std;
int main() {
    int A = 0, B = 0;//赢的次数 
    for (int i = 1; i <= 5; i++) {
        int x, y;
        cin >> x >> y;
        if(x==y) continue;
        if(x-y==-1||x-y==2) 
            A++;
        else 
            B++;
    }
    if(A>B)
        cout << 'A';
    else if (A<B)
        cout << 'B';
    else
        cout << 'D';
}

2. Problem B 角谷猜想

题目描述

角谷猜想:“对于任意一个大于1的自然数n,若n为奇数,则将n变为3n+1;若n为偶数则将n变为n的一半。经过若干次变换,一定能使n变为1。”
如n=6,共经过8次变换变为1,变换过程如下:
6->3->10->5->16->8->4->2->1。

输入

一个自然数n(n<=1000)。

输出

n变换为1的次数。

样例输入

6

样例输出

8

解题思路

用循环结构判断输入的n是否变成了1,如果不是1则操作次数加一,是一则输出其统计次数

#include<iostream>
using namespace std;
int main() {
    int n,ans = 0;
    cin >> n;
    while(n!=1){
        ans++;
        if(n%2) 
            n = 3 * n + 1;
        else 
            n /= 2;
    }
    cout << ans;
}

3. Problem C 数数字

状态

题目描述

把前n个正整数顺次写在一起构成序列:12345678910111213…。
请你数一数0~9各出现多少次。
如n=13,构成的序列为:12345678910111213。
其中0~9依次出现的次数为:1 6 2 2 1 1 1 1 1 1。

输入

第一行k:表示有k个正整数。
以下k行:每行一个n。

输出

共k行,每行10个数,依次对应相应输入的n对应的序列中的0~9的个数,中间用一个空格隔开。

样例输入

1
13

样例输出

1 6 2 2 1 1 1 1 1 1

解题思路

使用数组a来记忆0~9的个数,$$a[i]$$代表$$i$$的数量,对输入的$$n$$中的每个数字$$i$$进行统计:对10取余,得到末尾数字$$k,a[k]$$++,$$i/=10$$把末尾抹去,如此循环直到i变成$$0$$。

#include<iostream>
using namespace std;
int a[20];
int main() {
	int k, n;
	cin >> k;
	while(k--) {
		cin >> n;
		for (int i = 0; i <= 9; i++ ) //初始化 
			a[i] = 0;
		for (int i = 1; i <= n; i++) {
			int j = i;
			while(j) {
				a[j % 10]++;
				j /= 10;
			}
		}
		for (int i = 0; i < 10; i++ ) 
			cout << a[i] << " ";
		cout << endl;
	}
}

4.Problem D 小李发奖金

题目描述

当然打台球只是小李的休闲娱乐活动,对待他的本职工作,他还是非常兢兢业业的。但是小李的老板是个周扒皮,每次都想克扣小李的工资和奖金,甚至制定出非常奇葩的规则。
又到了每年发年终奖的时候了,今年老板的规则是这样的:给你n个数,每次你可以对任意一个数加1,直到所有的数都不相等为止,每加一次都要花费一定数额的费用。为了小李的幸福生活,聪明的你可否帮助小李,让他尽量少扣钱。

输入

第一行n(1<=n<=30000),表示共有n个数。
第二行共n个用空格隔开的非负整数ai(ai<=1000000)。

输出

仅一个整数,表示加到让每个数都不相等的最少次数。

样例输入

4
1 1 3 2

样例输出

3

提示

让1+1+1+1 = 4,给定的数字变成4,1,3,2。

解题思路

使用一个很大的数组num来存储标记出现的工资,比如出现100,那么$$num[100] = 1$$。这样在输入的过程中,如果发现当前的工资i在\(num\)里面标记其出现了,那么当前的数字i++,直到\(num[i]等于0\)再停止++。

#include<iostream>
using namespace std;
const int N = 1e7;//题目提示过的a[i]的范围
int num[N];
int main() {
	int n,ans = 0;
	cin >> n;
	for(int i = 1 ; i <= n ; i++) {
		int t;
		cin >> t;
		while(num[t]) {
			ans++;
			t++;
		}
		num[t] = 1;
	}
	cout << ans;
}

5. Problem E 小李打台球

题目描述

在异乡打拼的小李同志迷上了一款叫斯诺克的台球游戏,而且随着练习的深入,他总是能在某些神奇的时候开启外挂模式,此时小李指哪打哪,直至无球可打。现在小李想让你帮他计算下当他开启外挂模式的时候最多可以取得多少分数。
注意:台面上的球数经常会异于传统斯诺克。
斯诺克比赛的基本规则如下:
一、彩球共分8种颜色,红(1分)、黄(2分)、绿(3分)、棕(4分)、蓝(5分)、粉(6分)、黑(7分)、白(主球,控制白球大其余球)。
二、当台面上有红球的时候你必须先击打一个红球,然后只能击打一个彩球(不包括红球),此时落袋的彩球将会被放回桌面,一直重复该过程。
三、当打完规则二的彩球(不包括红球)发现已经没有红球时,按彩球的分值从高到低将其依次击入袋中。

输入

输入仅有一行,共7个用空格隔开的整数,分别为当前台面上红、黄、绿、棕、蓝、粉、黑球的数目。

输出

输出仅有一行,共1个整数,表示小李可以得到的最高得分。

样例输入

2 0 1 0 3 0 2

样例输出

48

提示

台面上共有红球2个、绿球1个、蓝球3个、黑球2个,获得最高分的打法是红-黑-红-黑-绿-蓝-蓝-蓝-黑-黑,共可以获得48分。
保证最后得分不超过2^31-1.

解题思路

依照题意,击打完红球后必须击打其他颜色球,并且这个球还放回桌面。所以我们可以查找得分最高的球的编号

  • 如果不是红球,则总得分是所有得分的总和 加上红球的数量*最高分
  • 如果是红球的话,则说明这次输入是只有红球的,按照规则2,击打完红球后,不能再击打红球,所以总得分是一定是1。
#include<iostream>
using namespace std;
long long int a[10];

int main() {
//	freopen("input.txt","r",stdin);
//	freopen("output1.txt","w",stdout);
	long long int sum = 0 , maxn = 0;
	while( cin >> a[1] ) {
		sum = 0,maxn = 0;//分别是总得分、球中的最高得分
		if(a[1]) maxn = 1,sum += a[1]*1;
		for(int i = 2 ; i <= 7 ; i++) {
			cin >> a[i];
			if(a[i]) 
                maxn = i,sum += a[i]*i; //直接加进总得分中,并更新最高得分
		}
		if(maxn!=1)
			sum += a[1]*maxn;
		else sum = 1; 
		cout << sum << endl;
	}
}

6. Problem F 小李打怪兽I

题目描述

小李对故乡的思念全部化作了对雾霾天气的怨念,这引起了掌控雾霾的邪神的极大不满,邪神派去了一只小怪兽去对付小李,由于这只怪兽拥有极高的IQ,它觉得直接消灭小李太没有难度了,它决定要和小李在智力水平上一较高下。我们可否帮助小李来战胜强大的怪兽呢?
问题是这样的:给定一堆正整数,要求你分成两堆,两堆数的和分别为S1和S2,谁分的方案使得S1S1-S2S2的结果小(规定S1>=S2),谁就将获得胜利。
注:S2可以等于0。

输入

第一行n(1<=n<=100),表示共有n个数
第二行共n个用空格隔开的正整数ai(ai<=100),表示给定的一堆正整数。

输出

输出就一个整数,表示 S1S1-S2S2 的最小值。

样例输入

4
1 2 3 4

样例输出

0

解题思路

所需求得的ans可以进行变换:

\[\begin{equation} \begin{split} ans=&s1^2-s2^2 \\=&(s1+s2)*(s1-s2) \\=&n*(s1-s2) \\=&n*(n-2*s2) \end{split} \end{equation} \]

依据此变化,所求ans只与s1-s2有关,让s2逼近n/2即可。

这题化用01背包的模板,不会的同学请先自行学习01背包。因为我们的目的是让s2逼近n/2,本质上就算从若干个背包(数字)中,选取价值和最接近n/2的背包,以此求得最接近n/2的数。

#include<iostream>
using namespace std;
const int N = 107, M = N*N;
int a[N],dp[M];
int main() {
	int n,sum = 0;
	cin >> n;
	for(int i = 1 ; i <= n ; i++) {
		cin >> a[i];
		sum+=a[i];
	}
	int half = sum/2;
	for(int i = 1 ; i <= n ; i++) 
		for(int j = half ; j >= a[i] ; j--) 
			dp[j] = max(dp[j],dp[j-a[i]] + a[i]);
		
	cout << sum*(sum-dp[half]*2);
}

7. Problem G 小李数星星

题目描述

小李在农村长大,那时候大家喜欢晚饭过后在院子里纳凉,听不懂大人在说什么的小李喜欢抬头看天空,尤其是夏天的夜晚,天上的星星又多又亮。
长大后小李进城打工,每当想家的时他还是喜欢抬头看看天,寻找另一边故乡的记忆。
可是大城市里空气质量太差了,雾霾天气横行,天上能看到的星星也越来越少了。
小李每次用一个正方形去覆盖自己所能看到的星星,随着日子的推移,这个正方形越来越小了,悲伤的小李希望你能告诉他这个正方形的面积。为了让问题变得简单,小李每次只会使用水平放置的正方形来覆盖(不会旋转),具体参照样例解释。

输入

第一行一个整数n,表示星星的数量。
接下来共n行,每行2个正整数(a,b),表示该星星到X轴距离为b,到Y轴距离为a,这些星星只会位于X轴的上方,Y轴的右方。
输入数据保证存在一个合法的正方形 (面积非零)去覆盖这些星星

输出

一个整数,表示能覆盖所有星星的最小正方形的面积。

样例输入

3
1 1
2 1
2 2

样例输出

1

提示

100%的数据,3<=n<=1000, 1<=x<=100000, 1<=y<=100000

解题思路

题目的本质是用一个方形覆盖所有坐标,我们需要到横向和纵向的最大坐标与最小坐标,并求取其差值,然后选取较大的差值去作为正方形的边长。本题数据范围略大,应当开long long

#include<iostream>
using namespace std;

int main() {
	int n ;
	cin >> n;
	int maxA = -1,minA = 0x7f7f7f7f;
	int maxB = -1,minB = 0x7f7f7f7f;
	for(int i =1 ; i <= n ; i++) {
		int a,b;
		cin >> a >> b;
		maxA = max(maxA,a);
		minA = min(minA,a);
		maxB = max(maxB,b);
		minB = min(minB,b);
	}
	long long l = max(maxA-minA,maxB-minB);
	cout << l*l;
}

8. Problem H 选组长

题目描述

Alice是班上的数学课代表,她现在想从班上选出两名组长来帮她收发作业(可以选Alice自己)。Alice的班级可以按区域分为四个大组,每个大组分别有a,b,c,d名同学,Alice在其中一个组中。为了避免收发作业时两名组长位置重叠,Alice决定这选出来的两名组长不能在同一个大组中。现在她想问你,她有多少种选组长的方式?

输入

第一行输入四个整数a,b,c,d

输出

输出包含一个整数,代表Alice可能的方案总数

样例输入

1 2 1 0

样例输出

5

提示

假设第一大组的同学为Alice,第二大组为Bob, Lucy。
第三大组为Tom。
则所有可能的组长为:Alice, Bob.
Alice, Lucy.
Bob, Tom.
Alice, Tom.
Lucy Tom.

数据范围与约定
题目包含10个测试点。
对于第1,2,3测试点,满足c = d = 0。
对于第4,5,6测试点,满足a,b,c,d ≤ 10000。
对于所有测试点,满足0 ≤ a,b,c,d ≤ 100000

解题思路

用每一组人数,和后面组的人数进行相乘(这样避免重复运算),得到选取此组与后面组时的方法数,枚举并求和即可。由于数据范围略大,应当开long long处理

#include<iostream>
using namespace std;
int main() {
	long long int a[5];
	long long sum = 0;
	cin >> a[1] >> a[2] >> a[3] >> a[4];
	for(int i = 1 ; i <= 3 ; i++) {
		for(int j = i+1 ; j <= 4 ; j++)
			sum+=a[i]*a[j];	
	}
	cout << sum;
}

9. Problem I 青蛙跳

题目描述

在数轴上有两只青蛙,它们都朝着正方向跳跃。 初始时,第一只青蛙在x1的位置,每秒向前跳y1个单位,第二只青蛙在x2的位置,每秒向前跳y2个单位。
试问:是否存在一个非负整数时刻使得两只青蛙停在同一个位置

输入

输入第一行,一个数n,表示一组数据中有n个询问
接下来n行,每行4个整数x1, x2, y1, y2,表示一个询问

输出

对于每个询问,如果存在两只青蛙停在同一个位置的时刻,输出YES,否则输出NO

样例输入

2
0 4 3 2
0 4 2 3

样例输出

YES
NO

提示

询问1:在时刻4,两只青蛙都停在了x=12的位置上
询问2:第二只青蛙的起点在第一只青蛙右边,且每次都比第一只青蛙跳的多,它们一定不会相遇

数据范围与约定

对于 30% 的数据, 0 ≤ x1, x2 ≤ 10, 1 ≤ y1, y2 ≤ 10
对于 50% 的数据, 0 ≤ x1, x2 ≤ 100, 1 ≤ y1, y2 ≤ 100
对于 70% 的数据, 0 ≤ x1, x2 ≤ 10^6, 1 ≤ y1, y2 ≤ 10^6
对于 100% 的数据, 0 ≤ x1, x2 ≤ 10^9, 1 ≤ y1, y2 ≤ 10^9, 1 ≤ n ≤10

解题思路

由题意,存在t满足:\(x1+y1*t=x2+y2*t\)

即为\(t=(x1-x2)/(y2-y1),(t\in N* )\) 。借此直接计算t是否满足正整数即可

#include<iostream>
using namespace std;
int main() {
	int n;
	cin >> n;
	while(n--) {
		int x1,x2,y1,y2;
		cin >> x1 >> x2 >> y1 >> y2;
		if ( (x1-x2)%(y2-y1) == 0 &&(x1-x2)/(y2-y1)>=0)
			cout << "YES" << endl;
		else cout << "NO" << endl;
	}
}
posted @ 2021-12-10 22:02  seekerHeron  阅读(304)  评论(0)    收藏  举报