实验室第一次排位赛习题分析
A-sky数:
熊熊学长发现了一个有趣的四位数2992,这个数,它的十进制数表示,其四位数字之和为2+9+9+2=22,它的十六进制数BB0,其四位数字之和也为22,同时它的十二进制数表示1894,其四位数字之和也为22,啊哈,真是巧啊。熊熊学长非常喜欢这种四位数,由于他的发现,所以这里我们命名其为Sky数。但是要判断这样的数还是有点麻烦啊,那么现在请你帮忙来判断任何一个十进制的四位数,是不是Sky数吧。
Input
输入含有一些四位正整数,如果为0,则输入结束。
Output
若n为Sky数,则输出“#n is a Sky Number.”,否则输出“#n is not a Sky Number.”。每个结果占一行。注意:#n表示所读入的n值。
Sample Input
2992
1234
0
Sample Output
2992 is a Sky Number.
1234 is not a Sky Number.
#include<stdio.h>
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
if(n==0)
break;
int sum1=0,sum2=0,sum3=0;
int b=n;
while(b)
{
sum1+=b%10;
b=b/10;
}
b=n;
while(b)
{
sum2+=b%12;
b=b/12;
}
b=n;
while(b)
{
sum3+=b%16;
b=b/16;
}
if(sum1==sum2&&sum1==sum3)
printf("%d is a Sky Number.
",n);
else
printf("%d is not a Sky Number.
",n);
}
return 0;
}
- 此题我将每个进制都分别写出,暴力求解
- 优化方法:利用函数代替单一重复,使用时传参即可
B-哥德巴赫猜想:
倩倩学姐想把一个偶数拆成两个不同素数的和,你有有几种拆法呢?
Input
输入包含一些正的偶数,其值不会超过10000,个数不会超过500,若遇0,则结束。
Output
对应每个偶数,输出其拆成不同素数的个数,每个结果占一行。
Sample Input
30
26
0
Sample Output
3
2
#include<stdio.h>
int main()
{
int a[10001],n;
for(int i=2;i<=10000;i++)
{
a[i]=i;
}
for(int i=2;i<=5000;i++)
{
for(int j=2*i;j<=10000;j+=i)
{
a[j]=0;
}
}
while(scanf("%d",&n)!=EOF)
{
if(n==0)
break;
int m=0;
for(int i=3;2*i<n;i++)
{
if(a[i]+a[n-i]==n)
++m;
}
printf("%d
",m);
}
return 0;
}
比赛时问题出在:
- int a[10001]定义成a[10000]导致数据存储不全出现WA
- 刚开始利用循环嵌套,时间复杂度较高出现TLE
解题思路与收获:
- 使用制表方法将10000以内的素数统计出来,不是素数的记为0,这样后续加法不会造成影响。为了提高制表速率,我们知道合数必定是一个数的整数倍(除了1和他本身)所以直接将2到5000的所有值的倍数剔除即可。
- 素数判断优化方法
C-这是什么呀:
现在我们有两个正整数A和B,请找出一个正整数C,使得式子( (A xor C)&(B xor C) )最小 (xor是异或运算)当然,如果使得式子最小的C为0时,请你输出1
Input
第一行输入T,代表有T组数据, 接下来每一行输入两个数A,B(A,B<=2^32)
Output
对于每一组数据输出一个正整数C
Sample Input
1
3 5
Sample Output
1
#include<stdio.h>
int main()
{
int t;
scanf("%d",&t);
long long a,b;
while(t--)
{
scanf("%lld%lld",&a,&b);
long long c=a&b;
printf("%lld
",c?c:1);
}
return 0;
}
解题思路与收获:
- 了解xor是异或运算,&是与运算,以及其性质
2.可以列出几个值之间的关系表格观察,表格在文末PPT里有,需要可以前往查看
D-倩姐的自我突破 :
倩倩学姐,一个大三的老阿姨呢,但是呢她还有一颗坚持竞赛的心。所以他碰到一个奥林匹克竞赛的数学问题,她搞不定很难受,决定暴饮暴食。聪明而优秀的学霸熊熊学长看到这一幕决定帮他解决这个问题。这个问题是:
我们描述 K!
k! = 1 * 2 * …* (k - 1) k
我们表示 S:
S = 1 * 1! + 2 * 2! + … +(n - 1) * (n-1)!
然后 S 对 n 去模是 ___________
你将得到一个整数n.
你需要计算 S 对 n 取模的值
输入
第一行输入一个整数 T(T < 1000), 表示测试用例的行数.
对于每个测试用例,都有一行包含一个整数 n.
输出
对于每个测试用例,打印一个整数 S 对 n 取模后的值.
提示
第一个测试用例 S = 1 1!= 1, 并且 1 的模 2 运算 1.
第二个测试用例 S = 11!+2 2!= 5 , 并且 5 对 3 取模是 2.
Sample Input
2
2
3
Sample Output
1
2
#include<stdio.h>
int main()
{
long long t;
scanf("%lld",&t);
while(t--)
{
long long n;
scanf("%lld",&n);
printf("%lld
",n-1);
}
return 0;
}
解题思路与收获:
- 首先这个题代码很短,没错,就是这么短小精悍,直接输入一个值,输出这个值-1就行。我尝试用循环,每次都超时,谁能想到这样都行。
- 所以学到了,找规律也很重要!下面是规律图
输入 | 输出 |
---|---|
2 | 1 |
3 | 2 |
4 | 3 |
5 | 4 |
… | … |
n | n-1 |
E-亲和数 :
古希腊数学家毕达哥拉斯在自然数研究中发现,220的所有真约数(即不是自身的约数)之和为:
1+2+4+5+10+11+20+22+44+55+110=284。而284的所有真约数为1、2、4、71、 142,加起来恰好为220。人们对这样的数感到很惊奇,并称之为亲和数。一般地讲,如果两个数中任何一个数都是另一个数的真约数之和,则这两个数就是亲和数。
你的任务就编写一个程序,判断给定的两个数是否是亲和数
Input
输入数据第一行包含一个数M,接下有M行,每行一个实例,包含两个整数A,B; 其中 0 <= A,B <= 600000 ;
Output
对于每个测试实例,如果A和B是亲和数的话输出YES,否则输出NO。
Sample Input
2
220 284
100 200
Sample Output
YES
NO
#include<stdio.h>
int main()
{
int M;
scanf("%d",&M);
while(M--)
{
int A,B,sum1=0,sum2=0;
scanf("%d%d",&A,&B);
for(int i=1;i<=A/2;i++)
{
if(A%i==0)
sum1+=i;
}
if(sum1==B)
{
for(int i=1;i<=B/2;i++)
{
if(B%i==0)
sum2+=i;
}
if(sum2==A)
printf("YES
");
else
printf("NO
");
}
else
printf("NO
");
}
return 0;
}
简单就不过多赘述了。
F- 熊熊的尝试 :
熊熊学长一天在实验室里闲的没事。他想做点游戏打发一下时间。他就拉上了和他同样无聊的柴柴学长。
两位学长要玩的游戏是什么呢?很简单,它是这样定义的:
1、 本游戏是一个二人游戏;
2、 有一堆石子一共有n个;
3、 两人轮流进行;
4、 每走一步可以取走1…m个石子;
5、 最先取光石子的一方为胜;
如果游戏的双方使用的都是最优策略,请输出哪个人能赢。
Input
输入数据首先包含一个正整数C(C<=100),表示有C组测试数据。每组测试数据占一行,包含两个整数n和m(1<=n,m<=1000),n和m的含义见题目描述。
Output
如果先走的人能赢,请输出“first”,否则请输出“second”,每个实例的输出占一行。
Sample Input
2
23 2
4 3
Sample Output
first
second
#include<stdio.h>
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int a,b;
scanf("%d%d",&a,&b);
if(a%(b+1)==0)
printf("second
");
else
printf("first
");
}
return 0;
}
解题思路与收获:
- 这个代码也很简短,主要涉及到博弈论,有先手必胜态和后手必胜态(双方都想要赢,不要想什么放水)
- 当剩余m+1时,无论先手怎么取,都是后手胜。
- 所以当刚开始的数量等于m+1的整数倍时,在先手取完后,后手都可以让其重新恢复m+1的整数倍,所以后手必胜。
- 若刚开始不是m+1的整数倍,那么先手总可以让其变为m+1的整数倍,所以先手必胜。
G-数塔问题:
有如下所示的数塔,要求从顶层走到底层,若每一步只能走到相邻的结点,则经过的结点的数字之和最大是多少?
图片:
Input
输入数据首先包括一个整数C,表示测试实例的个数,每个测试实例的第一行是一个整数N(1 <= N <= 100),表示数塔的高度,接下来用N行数字表示数塔,其中第i行有个i个整数,且所有的整数均在区间[0,99]内。
Output
对于每个测试实例,输出可能得到的最大和,每个实例的输出占一行。
Sample Input
1
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
Sample Output
30
#include<stdio.h>
int main()
{
int C;
scanf("%d",&C);
while(C--)
{
int N;
scanf("%d",&N);
int a[100][100];
for(int i=0;i<N;i++)
{
for(int j=0;j<=i;j++)
{
scanf("%d",&a[i][j]);
}
}
for(int i=N-1;i>0;i--)
{
for(int j=0;j<N-1;j++)
{
a[i-1][N+j]=(a[i][j]>a[i][j+1]?a[i][j]:a[i][j+1])+a[i-1][j];
a[i-1][j]=a[i-1][N+j];
}
}
printf("%d
",a[0][0]);
}
return 0;
}
解题思路与收获:
- 从下到上取分支的两个数中的最大值加到共同连接的上一层,此时上一层的新值为最优解,以此类推,最下层判断完后,此时上一层都为各自路径的最优解,再以同样方法向上走,最终取得全局最优解。
- 合理利用二维数组
数塔详解请点击这里
H-Tyloo的S1mple本人 :
熊熊是一名csgo玩家,在沙二驰骋多年的他,显然已经对这个地方了如指掌。他恐怖的定位和风骚的身法以及高超的战术让他的队友后悔来到这个地方。尤其是他的大狙,当他扛着笨重的awp走到中门时,每一声枪响都会有人应声倒地。但是熊熊有个奇怪的癖好,他每一次只杀奇数个数的人,一杀他嫌太少,九杀又太多,所以他每一局他只会打出三杀,五杀或七杀。现在你在他旁边看他打游戏,你看到他杀了n个人,你现在想知道他分别打出了多少个三杀,五杀或七杀。
Input
第一行的整数 t(1<=t<=1000)— 测试用例的个数.每个测试用例只有一个输入数据 — lrh杀人的总数 n(1<=n<=1000)
Output
如果对于某个测试样例,没有正确的答案,则输出 -1.否则,输出3个正整数-三杀的个数,五杀的个数,七杀的个数 — 如果存在多个情况,输出任意一种即可
Input
4
30
67
4
14
Output
2 2 2
7 5 3
-1
0 0 2
#include<stdio.h>
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n,sum;
scanf("%d",&n);
for(int i=0;i<=n;i+=7)
{
for(int j=0;j<=n;j+=5)
{
sum=i+j;
if(sum>n)
break;
if((n-sum)%3==0)
{
printf("%d %d %d
",(n-sum)/3,j/5,i/7);
goto ABC;
}
}
}
printf("-1
");
ABC:;
}
return 0;
}
解题思路与收获:
- 这个题有多个答案,题中说只需任意一组即可,故只要满足3a+5b+7*c=n即可
- 利用循环嵌套,如果满足,直接输出并跳到下个数据输入。
J-最后是啥呢:
每次zwt可以选择两个不同的数字,并且大喊一声“フュージョン!!!”,然后这两个数字就会融合,变成他们和的一半(向上取整)放到数组里
现在有一个序列1,2,3,4,………,n,zwt想知道他要施法几次,每一步选择哪几个数字,这个数组才能变成一个数,并且使得这个最后留下的数字最小
如n=4,
1.选择a=2,b=3,数组变为[1,3,3]
2.选择a=3,b=3,数组变为[1,3]
3.选择a=1,b=3,数组变为[2]
如果有多种方案,输出任意一种
Input
第一行输入位t[1,1000],表示有t组数据
每组数据的第一行为n[2,200000]
n的总和不会超过200000
Output
对于每组数据输出最后留下的最小的那个数字,之后n-1行依次输出步骤
Example:
Input
1
4
Output
2
2 4
3 3
3 1
#include<stdio.h>
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n,c=0;
int a[200001],b[200000][2];
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
a[i]=i;
}
for(int i=n;i>1;i--)
{
b[c][0]=a[i-1];
b[c][1]=a[i];
c++;
if((a[i]+a[i-1])%2==0)
a[i-1]=(a[i]+a[i-1])/2;
else
a[i-1]=(a[i]+a[i-1])/2+1;
if(i==2)
printf("%d
",a[i-1]);
}
for(int i=0;i<c;i++)
{
printf("%d %d
",b[i][0],b[i][1]);
}
}
return 0;
}
解题思路与收获:
- 此题解法很多,形式比较自由。我使用的是官方推荐,即从后往前不断求平均数(注意此题向上取整!)。还有一种即随机选取.