题目列表:
A 小粉兔的礼物
B 《粉兔本纪》
C 我要成为无畏契约高手!
D 小粉兔的位运算游戏
E 小粉兔的点名册
F 兔,我看到了!
G 这是一个字符串问题吗?
H 小粉兔的车站遐想
A 小粉兔的礼物
题面:
思路:
签到题,此处不作过多说明(注意标点符号为中文)
B 《粉兔本纪》
题面:
思路:
通过超链接的文章信息我们可以得知:
问题一的答案为:陈亮舟
问题二的答案为:5(分别为:导师-小粉兔 小粉兔 谷洛兔 小粉兔(小号) pinkrabbit)
问题三的答案为:福建省
注意题目要求输出格式!!!
C 我要成为无畏契约高手!
题面:
思路:
以靶心为圆心,半径为i的圆作为第i个圈,击中此圈内的得分为11-i。
根据这一点,我们只需找出击中位置在第几圈即可,而靶的中心为(0,0),我们则得到了击中位置的坐标,我们自然而然可以通过距圆心位置的距离来判断当前处在第几圈。
(原题为牛客周赛round16的B题)
代码展示:
#include<stdio.h>
int main(){
int x,y,i;
scanf("%d %d",&x,&y);//x为横坐标,y为纵坐标
for(i=1;i<=11;i++)//设当前为第i圈,圆的半径为i,距圆心的位置就是i*i,总共有11圈
{
if((x*x+y*y)<=i*i) //判断根据两点距离公式计算
{
printf("%d",11-i);//如果击中位置在当前圈内,直接输出得分并结束程序
return 0;
}
}
printf("0");//如果击中位置不在任何一圈,得分为0
}
D 小粉兔的位运算游戏
题面:
思路:
首先我们需要知道的是,位运算是对两个数的二进制每一位进行的运算。
对于二进制,从最低位到最高位,分别代表2的0,1,2,3,....次方
如果我们运用"&" 运算,那么两个数二进制位的相同位置就会进行下面的规则:
1&0=0,0&1=0,0&0=0,1&0=0;(两个都为1,才有1)
如5&1=(101)&(001)=(001)=1 //()内为二进制形式
如果是"|"运算,那么:
1|1=1,0|1=1,1|0=1,0|0=0;(一个有1就为1)
如5|1=(101)|(001)=101=5
如果是 "^"运算, 那么:
1^1=0,0^0=0,1^0=1,0^1=1;(相同为0,不同为1)
如5^1=(101)^(001)=(100)=4
知道了这一点,我们再分析题目,先手有机会选择三个符号,而他只有这三种运算中出现唯一的最大值选择对应的运算才能获胜。
而二进制位中,二进制位1的个数和位数更高,一个数就更大,我们想得到最大值,就需要让每个二进制位都尽可能得到1,在上面的运算中,"|" 运算每次得到的值一定最大,其他两个最好的情况也只是跟它同大
所以我们只需判断 "|"运算的结果是否比其他两个符号大
当然,我们也可以直接求出三个运算的值,然后按照三个值比较大小来做。
代码展示:
#include <stdio.h>
int main()
{
int t;
scanf("%d",&t);
while(t--){
int x,y;
scanf("%d%d",&x,&y);
if((x|y)>(x^y)&&(x|y)>(x&y))//位运算的优先级比>,<,==要低,所以应当加上括号提高运算优先级
{
printf("YES\n");
printf("| %d\n",x|y);
}
else{
printf("NO\n");
}
}
return 0;
}
E 小粉兔的点名册
题面:
思路:
记人数总和sum为0 , 对于每次输入的x直接加上,在过程中只要sum小于0,就说明记录有误(提示样例有这种情况的例子)。
代码展示:
#include<stdio.h>
int main()
{
int n;
scanf("%d",&n);
int sum=0;//当前场馆内总人数
int flag=1;//作个标记,记录中途记录是否有误
for(int i=0;i<n;i++){
int x;
scanf("%d",&x);
sum+=x;
if(sum<0){//如果场馆内人数为负则记录有误
flag=0;
}
}
if(flag)
{
printf("%d",sum);
}
else printf("-1");
return 0;
}
F 兔,我看到了!
题面:
思路:
通过分析题意,我们可以推断,一个上线('+')的通知可能代表着两种情况:
1.这个人从未上线过
2.这个人之前上线过,但他下线后又上线了
如果这个人从未上线过,那么我们就多得到了一个人上线的情况;
如果这个人之前上线过,那么他对我们的总目标(每个粉丝都上线)没有意义
而我们无法具体分析是哪个人上线和下线
所以我们应该考虑总的情况
在这个题目中,我们有三种答案,
其中输出"YES"的情况为,在k则通知的过程中,如果每个粉丝都在线(即过程中上线人数==粉丝数),那么条件就符合;
其中输出"MAYBE"的情况为,我们开始有m个人(用num存储一下)在线,过程中有up个上线通知,这up个可能就代表上述'+'的第二个情况,如果过程中未达成"YES"的条件,我们就需要 考虑存在'+'的每个都为第一个情况,那么最后我们只需要判断刚开始在线的人数num+up的个数是否大于等于n;
而当这两种情况都不符合,自然输出"NO"就可以了
代码展示:
#include <stdio.h>
void solve()
{
int n, m, k;
scanf("%d%d%d\n", &n, &m, &k);//一共n个粉丝,当前在线m个,有k个通知,数字和字符挨着输入时要注意吸收空格,加上"\n"或者getchar()都可以
int flag = 0;//标记每个粉丝是否都看过了
int up = 0;//过程中上线数的数量
int num = m;//m为每个时刻在线的人数,num存储一下,表示开始有多少人在线
char s;
for (int i = 0; i < k; i++)
{
scanf("%c",&s);//每次读入一个通知
if (m == n)//如果过程中在线的人数等于n,则每个粉丝都在线了,符合题目要求
{
flag = 1;
}
if (s== '-')//如果有人下线,更新当前在线的人数
{
if (m - 1 < 0)//防止m为负数
{
m = 0;
}
else
{
m -= 1;
}
}
else if (s== '+')
{
if (m + 1 > n)//防止人数超标
{
m = n;
}
else
m += 1;
up++;//过程中上线过的人数+1
}
}
if (m == n)//因为最后一次没判断,在这里再次判断
{
flag = 1;
}
if (flag)//如果过程中有n个粉丝同时在线
{
printf("YES\n");
}
else if (num + up >= n)//如果从开始到结束可能有n个粉丝同时在线
{
printf("MAYBE\n");
}
else//否则人数不足
{
printf("NO\n");
}
}
int main()
{
int t;
scanf("%d", &t);
while (t--)
solve();
return 0;
}
G 这是一个字符串问题吗?
题面:
思路:
题目的意思很简单,问题在于如何存储输入的数据,以及找到要输出的内容
题目中给出了超链接,讲了字符串函数的使用,
这里我们只需要用到gets和strlen函数即可
将题目给出的输入存下来当成一个字符数组,因为题目规定字符数组里只有字母,数字只有下标的值,我们将字符数组遍历一遍,是数字的用来计算下标,是字母的存入一个数组内,最终按照数组输出值的形式即可。
对于下标的记录,因为我们得到的数不一定是一个一位数,并且我们是依次从左到右读入数据,每次我们一定先读入最高位,那么我们在读入下一位时,将num乘10,再加上下一位的数,就可以读入两位数或者三位数了。
代码展示:
#include <stdio.h>
#include <string.h>
int main()
{
char s[110],ans[110];//s数组存储初始数据,ans存储输入中的字符数组
gets(s);//输入题目给的字符串
int l = strlen(s);//得到输入字符串的长度
int num=0;//num记录得到的下标的值
int p=0;
for(int i=0;i<l;i++){
if(s[i]>='0'&&s[i]<='9'){
num=num*10+(s[i]-'0');//因为数字高位固定在前,我们每次将num*10再加上当前数字,就可以得到两位数甚至三位数的下标值了
}
else if(s[i]!='['&&s[i]!=']'){//根据题目所给,除了数字和 []就是字母,我们只需要从前到后存起来就好
ans[p++]=s[i];//等价于ans[p]=s[i],p++; 两步合为一步
}
}
printf("%c",ans[num]);
return 0;
}
H 小粉兔的车站遐想
题面:
思路:
首先分析题目,一共有0~n这么n+1个数,而我们最开始得到的是其中的n个数
再分析mex的操作,它找出的是{}内未出现过的数的最小值
而我们每天得到的是这n+1个数的n个数,对这n个数进行mex操作,得到的必然是当前未出现的那个数的值
例如n=4,这n+1个数应该为:[0,1,2,3,4]
假设我们刚开始得到的是{1,2,3,4},那么此时对这个进行MEX操作,得到的必然是0
同时用0替换第一位,变成{0,2,3,4},接着以这样的操作替换后面的数,就会得到{0,1,2,3}
多次重复这样的操作,我们就会发现下面的规律
每一次进行mex对原数组内的每个数依次替换时:
{1,2,3,4}
{0,1,2,3}
{4,0,1,2}
{3,4,0,1}
{2,3,4,0}
{1,2,3,4}
其实就是将{1,2,3,4,0,1,2,3,4}的后四位依次向前移动一位
而我们能注意到移动到n+1次后,就将回到{1,2,3,4},也就是原数组的样子
到此为止,我们只需找出第一次未出现的数,然后构建出一个2*n+2大小的循环数组,最后找到开始遍历的位置,即可输出最终的n个数
如何找到第一次未出现的数呢?
我们用一个数组s来记录每个数的状态,初始每个数在s里的值都为0,如果后面这个数出现了,我们把它的值变为1,这样,我们再遍历0~n的S[i],就能知道哪个i没出现过
代码展示:
#include <stdio.h>
void solve()
{
int n, k;
scanf("%d%d", &n, &k);//n为车牌号的最大值以及每天的车辆数,k为天数
k %= (n + 1);// 每经过n+1天就回到最开始的样子,所以中间重复的部分可以忽略不计
int a[2 * n + 2];//存储循环数列的样子
int s[n + 1];//记录每个牌号是否出现过
for (int i = 0; i <= n; i++)
{
s[i] = 0;
}
int mex = 0;//mex记录n个数字里没出现过的值
for (int i = 0; i < n; i++)
{
scanf("%d", &a[i]);//数组a的0~n-1为最开始的样子
s[a[i]] = 1;//出现过的数字进行标记
}
for (int i = 0; i <= n; i++)
{
if (!s[i])//如果i没出现过,用mex记录一下
{
mex = i;
break;
}
}
a[n] = mex;
for (int i = n + 1; i <= n * 2; i++)
{
a[i] = a[i - n - 1];//填充后n个元素,方便循环求解
}
for (int i = n + 1 - k, p = 0; p < n; p++, i++)//依次向前循环求解,n+1-k其实就是原数组向前移动了多少位,接着从这位开始向后输出够n个元素即可
{
printf("%d%c", a[i], " \n"[p == n - 1]);//" \n"[p==n-1]等价于前n-1位输出空格,最后p==n-1时换行
}
}
int main()
{
int t;
scanf("%d", &t);
while (t--)
solve();
return 0;
}