HNUCM2018年新生赛重现赛——2020新生赛
签到题:J
简单题:A,B,C,G,H
中等题:I(代码),E,F
困难题:I(证明),D
A: XP的素数
题目描述
XP最近对素数很痴迷,特别是那些特殊的素数,其中有一类素数被称为孪生素数。其定义如下:如果一个数k是素数,k+2也是素数,那么k和k+2成为一对孪生素数。请计算一个给定区间m和n(0<m<n)中孪生素数对的个数。
输入
单组输入数据
m n
(0<m<n<1000)
输出
请输出一行结果:区间[m,n]中孪生素数对的个数
样例输入
1 999
样例输出
35
代码
先写一个判断素数的函数在分别判断i和i+2是否为素数。但是这个题用筛法求素数应该是最好的
#include<stdio.h>
int f(int n)
{
int i;
for(i=2;i*i<=n;i++)
if(n%i==0)
break;
return n>i;
}//判断是否为素数
int main()
{
int m,n,j=0,i;
scanf("%d%d",&m,&n);
for(i=m;i<=n-2;i++)
{
if(i==1)
continue;
if(f(i)&&f(i+2))
j++;
}
printf("%d\n",j);
return 0;
}
B:XP的小视频
题目描述
XP的表哥为他推荐了一些学习计算机编程的小视频,这些视频的播放时间长短不完全相同。现在给定一段时间,你能告诉XP他最多可以看多少个视频吗?每个视频的播放时间和给定的总时间均用分钟为单位。
输入
单组输入数据
第一行为m n
m为给定时间长度(分钟)(0<n,m<=1000)
n表示视频个数
接下来是n个整数代表每个视频的播放时间(每个视频播放时间为不超过1000的正整数)
输出
输出一个整数代表XP最多可以看的视频数。
样例输入
84 6
65 46 18 76 79 3
样例输出
3
代码
和题目名称一样,先尽量看小的视频,保证看得最多
#include<stdio.h>
int a[1000];//定义全局变量
void sort(int n1,int n2){
int i,j,t;
for (i=n1;i<n2;i++)
for (j=i+1;j<n2;j++)
{
if (a[i]>a[j])
{
t=a[j];
a[j]=a[i];
a[i]=t;
}
}
}
void solve(int m,int n){
int t=0,i,s=0;
sort(0,n);//调用sort函数对视频长度进行排序
for(i=0;i<n;i++){
s+=a[i];
if(s<=m){
t++;
}
else{
break;
}
}
printf("%d\n",t);
}
int main()
{
int mm,num,i;
scanf("%d %d",&mm,&num);
for(i=0;i<num;i++){
scanf("%d",&a[i]);
}
solve(mm,num);
return 0;
}
C: XP的三角形
题目描述
XP最喜欢的图形是三角形,最近他在研究直角三角形,他想到这么一个问题,如果三边长{a,b,c}均为整数的直角三角形周长为p,(a<=b<=c)当p = 120时,恰好存在三个不同的解:{a=20,b=48,c=52}, {a=24,b=45,c=51}, {a=30,b=40,c=50}。现在给定一个p求不同的解的个数。(p<=1000)
输入
单组输入数据
p
输出
输出一行结果
样例输入
120
样例输出
3
代码
暴力模拟也要一点技巧减少下运行时间,因为a最小,所以它一定小于等于p/3,注意p/3是向下取整的,实际数学上的话是要小于的P/3的,同理b就是小于等于p/2,a和b长度最少为1,那c最大也就c-a-b
#include<stdio.h>
int main()
{
int n=0,a,b,c,p;
scanf("%d",&p);
if(p<=1000)
for(a=1;a<=p/3;a++)
for(b=a;b<=p/2;b++)
for(c=b;c<=p-a-b;c++)
if(a+b+c==p&&a*a+b*b==c*c)
n++;
printf("%d\n",n);
}
D:XP的全排列
题目描述
xp想用1-9九个数字组成数学公式:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oyDG5dOx-1667322448512)(https://acm.hnucm.edu.cn/upload/pimg_2018xss_02.png)]
其中ABCDEFGHI每一个字符均对应一个不同的数字,你能帮xp输出所有的组合吗?
输入
输出格式为
ABCD/EFGHI
每个结果占一行
输出
输出结果按分子大小降序
代码
不怕各位大佬笑话,这题今天写的时候错了好几次,刚开始是没有注意要输出顺序的全排列,而且写输出顺序的全排列代码也错了好几次,然后是以为它要约分,最后发现它是要输出分数值为1/3的排列
题目意思很简单,我们只需要保证求abcdefghi这个全排列的时候,下一个排列刚好比当前排列的大的排列就行了,比如123456789下一个就是123456798,132456897下一个是132456978。
全排列我知道的有两种写法,一种是顺序填充的写法,还有种是交换首位元素的方法,第一种求出来的全排列是顺序的,下面用第一种求,看不懂的同学可以先去看看dfs书写全排列再来阅读下面的代码。代码还有一些瑕疵,比如空间可以省一些
dfs类题都是递归写好,递归出错很麻烦,学会调试或者在特殊语句后面加上输出查错会轻松很多
#include<stdio.h>
#include<string.h>
int vis[10], nums[10];
int a[500000], b[500000];
int len;
void perm(int cur)
{
if (cur == 10)
{
for (int i = 1; i <= 4; i++)
a[len] = a[len] * 10 + nums[i];
for (int i = 5; i <= 9; i++)
b[len] = b[len] * 10 + nums[i];
len++;
return;
}
else
{
for (int i = 1; i <= 9; i++)
{
int flag = 1;
for (int j = 1; j <cur; j++)
{
if (nums[j] == i)
{
flag = 0;
break;
}
}
if (flag)
{
nums[cur] = i;
perm(cur + 1);
}
}
}
}
int main()
{
perm(1);
for (int i = len-1; i >=0; i--)
if (b[i] / a[i] == 3 && b[i] % a[i] == 0)
printf("%d/%d\n", a[i],b[i]);
return 0;
}
E:XP的演讲比赛
题目描述
XP参加了学校的演讲比赛,比赛共分为两轮,每一轮比赛得分各占总得分的50%。在最终结果公布之前,XP只知道在两轮的排名情况,但不知道具体分数,最终结果将按照两轮总得分升序排列。聪明的你能够告诉XP,他最喜欢的G在最终排名中可能得到的最好名次和最差名次吗?
输入
第一行输入n,表示参赛选手的人数(n<=26)
第二行输入n个字符,表示第一轮的排名情况
第三行输入n个字符,表示第二轮的排名情况
输出
输出一行 两个数分别为G的最好名次和最差名次,用空格隔开
样例输入
10
A B C D E F G H I J
A C I G E F J H D B
样例输出
3 8
第一场赢了某些人,第二场也赢了某些人,这两次比赛都比G排名低的人,在最终排名必然比G低,同理,第一场和第二场都比G排名高的人,那这些人必然在最终排名时超过G
注意输入问题,空格和回车怎么解决?大家可以试试直接用scanf之类的
#include<stdio.h>
int main()
{
int n;
int a[2][26]={0};//a[0][name-'A']代表name赢过G几次,a[1][name-'A']代表name输过几次
char ch[26] = { 0 },c;
int pos = 0;
scanf("%d", &n);
for (int i = 0; i < n;)
{
c = getchar();
if (c >= 'A' && c <= 'Z')
{
ch[i] = c;
if (c == 'G')
pos = 1;
else
a[pos][c - 'A']++;
i++;
}
}
pos = 0;
for (int i = 0; i < n;)
{
c = getchar();
if (c >= 'A' && c <= 'Z')
{
ch[i] = c;
if (c == 'G')
pos = 1;
else
a[pos][c - 'A']++;
i++;
}
}
int max = 0, min = 0;
for (int i = 0; i < n; i++)
{
if (a[0][i]==2)//两场都赢过G
max++;
if (a[1][i]==2)//两场都不如G
min++;
}
printf("%d %d\n", max+1,n-min);//注意n-min和max+1才是名次
return 0;
}
F:XP的座次表
题目描述
XP迎来了他大学第一堂期末考试,老师开始安排考场座次,第一列从第一个同学开始从前往后按照学号(学号从1开始递增)升序排列,然后第二列接着第一列最后一个同学的学号从后往前按照学号升序排列,第三列又从第一个同学开始接着第二列的第一个同学的学号从前往后按照学号升序排列,…,依次类推。为了简化问题,我们假设考场有n行n列,你能够帮XP编写一个程序生成考场座次表吗?
输入
单组输入数据
n(n<=20)
输出
一个n行n列的座次表,注意每列的学号要向右对齐,且行末不要有多余的空格
样例输入
4
样例输出
1 8 9 16
2 7 10 15
3 6 11 14
4 5 12 13
代码
这个格式就真的离谱,挺考验分类讨论的
看代码自己一步步分析过去
#include<stdio.h>
int main()
{
int n;
scanf("%d",&n);
int a[n][n];
int i,j;
int ans=1;
int m,max1[20];
m=-1;
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
{
a[j][i]=ans;
if(m<a[j][i])
m=a[j][i];
ans++;
}
max1[i]=m;
}
int t;
for(i=1;i<n;i+=2)
{
for(j=0;j<n/2;j++)
{
t=a[j][i];
a[j][i]=a[n-j-1][i];
a[n-j-1][i]=t;
}
}
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
{
m=max1[j]/10;
if(m>=1000)
printf("%6d",a[i][j]);
else if(m>=100)
printf("%5d",a[i][j]);
else if(m>=10)
printf("%4d",a[i][j]);
else if(m>=1)
{
if(j!=0)
printf("%3d",a[i][j]);
else
printf("%2d",a[i][j]);
}
else if(m==0)
{
if(j!=0)
printf("%2d",a[i][j]);
if(j==0)
printf("%d",a[i][j]);
}
}
if(i!=n-1)
puts("");
}
return 0;
}
G:XP的点滴
题目描述
XP一不留神感冒了,于是跑到校医院打点滴。打点滴真是无聊啊,他看到盐水一滴一滴地滴下来,突然想到一个问题:如果盐水有规律地滴下,先滴一滴,停一下;然后滴二滴,停一下;再滴三滴,停一下…,假设这瓶盐水一共有n毫升,每一滴是y毫升,每一滴需要的时间是一秒(假设最后一滴不到y毫升,需花费的时间也算一秒),停一下的时间也是一秒。请问XP多久能挂完这瓶盐水呢?
输入
组输入数据
n y (0<n,y<=1000)
输出
输出一行结果
样例输入
10 1
样例输出
13
水题
给的代码是我去年写的一个代码,取其精华去其糟粕就行
#include<stdio.h>
void solve(int n,int y){
int i,t1,s=0,sum=0;
double t;
for(i=1;;i++){
if(n-sum>i*y){
sum+=i*y;
s+=i;
}else{
t=(n-sum)*1.0/y;
t1=(int)t;
if(t==t1){
s+=t1;
}else{
s=s+t1+1;
}
sum=n;
}
if(sum>=n){
break;
}
s++;
}
printf("%d\n",s);
}
int main()
{
int num,yy;
scanf("%d %d",&num,&yy);
solve(num,yy);
return 0;
}
H:三家人
题目描述
有三户人家共拥有一作花园,每户人家的太太均需帮忙整理花园。A太太工作了 5天,B太太则工作了 4天,才将花园整理完毕。C 太太因为正身怀六甲无法加入她们的行列,便出了 90 元。请问这笔钱如何分给 A、B二位太太较为恰当?A应得多少元?
90/(5+4)*5=50元?如果这么想你就上当了!正确答案是 60元。如果没想通的话再想想吧。 下面回答一个一般性的问题:假定 A 太太工作了 x 天,B 太太工作了 y 天,C 太太出了 90 元,则 A太太应得多少元?输入保证二位太太均应得到非负整数元钱。
输入
输入第一行为数据组数
T
( T <=20)。每组数据仅一行,包含三个整数
x ,
y ,
z
(1<= x ,
y <=10,
1<= z <=1000)。
输出
对于每组数据,输出一个整数,即 A太太应得的金额(单位:元)。
样例输入
2
5 4 90
8 4 123
样例输出
60
123
提示
本题有个小小的陷阱哦。如果答案错的话,认真检查一下代码吧。
代码
湖南第八届省赛的一个题
听坑的一个题,弄清楚样例就知道了
我们要求的是C太太应该怎么分配这些前给他们,我们要看的不是A,B两位太太的上班总天数,而应该看他们帮C太太做了几天
拿样例讲吧,每位太太应该都要做3天,现在A太太做了5天,比她实际要做的多了2天,这两天就是帮C太太做的,同理C太太还有一天的班是B太太给她上的,所以实际比例应该为2:1,A太太得到60元。
#include<stdio.h>
int main()
{
int t,x,y,money;
float s;
scanf("%d",&t);
while(t--)
{
scanf("%d%d%d",&x,&y,&money);
s=(x+y)*1.0/3;
printf("%.0f\n",(x-s)/s*money);
}
return 0;
}
I:XP的翻转
题目描述
给定两组长度相等的字符串由‘0‘和’1‘组成,分别为初始态和目标态。
每次可以选择任意的区间翻转(区间内的‘0’->‘1’,‘1’->‘0’)。
问从初始态到目标态最少需要多少次。
输入
多组数据。
每组数据为两行字符串,分别表示初始态s和目标态t。(0<|s|=|t|<=100000)
输出
对于每组数据,输出一个整数表示答案
样例输入
01
01
01
10
样例输出
0
1
代码
这题可能有些同学能想到怎么做,但是不知道为什么这么做。今天刚做的时候我也是一下子想到怎么做,然后用了一定的时间才证明出来为什么可以这么做
先说做法:每次反转每个连续不相等区间就行了,也就是s有多少个连续不相等区间,答案就是多少
证明:
-
我们可以把s序列看成和t对应相等的子序列与和t对应不相等的子序列交替存在,比如001101和000000,连续相等的为[1,2],[5,5],连续不相等的为[3,4],[6,6],很容易可以想到,在最长连续不相等的序列中选取子序列进行反转就是多次一举,因为选取之后只会让连续不相等的子序列不变甚至变多,这样我们需要变换的区间反而变多了,这不是我们希望看到的。因此我们进行反转的最小单位为对应连续的最长不相等子序列或者对应连续的最长的相等子序列
-
接着是两条最重要的性质:在边界上的对应连续相等的序列删除之后不会对结果造成任何影响;边界上存在对应连续不相等的序列必须单个反转(即不能加上其他的连续对应相等或不相等子序列一起反转)
-
第一条性质不加以证明,接下来证明第二条:反证,如果我选取其他的子序列和边界上的连续不等序列一起反转,因为这些序列都是交替出现的,我们把边界的序列反转成了连续相等序列,根据第一条性质,这不会对结果造成影响,但是我们会发现原来和边界序列相邻的连续相等序列变成了连续不等序列,并且成了新的边界序列。这样会造成一个结果,如果边界不相等序列不单个反转,只会产生其他的边界不相等序列,相反,如果单个反转,我们甚至可以去除边界上一个不相等子序列和一个相等子序列。
-
由此我们每次都从边界上删除不相等的子序列,最终结果就是s中不相等子序列的数量
贪心的描述不好搞,如有不解或错误处可以指出,上代码
代码中查找不相等区间有多少个,没有使用双指针写法,而是判断有多少个临接点使得临接点前处于不相等序列,临界点处处于相等序列,当然,如果右边界序列是不相等的,最终结果需要加一
#include<stdio.h>
#include<string.h>
char s[100010], t[100010];
int main()
{
while (scanf("%s%s", s, t) != EOF)
{
int len = strlen(s);
int ans = 0;
for (int i = 1; i < len; i++)
{
if (s[i] == t[i] && s[i - 1] != t[i - 1])
ans++;
}
if(s[len-1]!=t[len-1])
ans++;
printf("%d\n", ans);
memset(s, 0, sizeof(s));
memset(t, 0, sizeof(t));
}
return 0;
}
J: XP的幸运数
题目描述
XP的幸运数是3,如果一个数能够被3整数,他也认为是一个幸运数,因为这个数字可以由很多个3组成。现在输入一个数,判断该数是否是XP的幸运数。如果是输出:Yes,如果不是则输出:No。
单组数据。
输入
一个正整数N(N<100000)。
输出
Yes或者No(输出结果占一行)。
样例输入
3
样例输出
Yes
水题
#include<stdio.h>
int main()
{
int N;
scanf("%d",&N);
if(N%3==0)
printf("Yes\n");
else
printf("No\n");
return 0;
}