海事大学C语言竞赛解题报告初赛.docx
《海事大学C语言竞赛解题报告初赛.docx》由会员分享,可在线阅读,更多相关《海事大学C语言竞赛解题报告初赛.docx(16页珍藏版)》请在冰豆网上搜索。
海事大学C语言竞赛解题报告初赛
第五届微软编程大赛初赛解题报告
--ByPeterJC
01SkyAo的节操
题目描述Description
SkyAo决定做个程序来查看自己的节操变化量:
输入四个数字,如果是严格递增的数,那么输出JieCaoUp,严格递减的话,输出JieCaoDown,如果四个数字一样,那么输出JieCaoConstant,否则的话输出WhereisJieCao?
输入描述InputDescription
输入会是四个正整数,代表节操量读入。
每个整数会占一行。
样例输入SampleInput
30102020
输出描述OutPutDescription
JieCaoUp,JieCaoDown,JieCaoConstant,WhereisJieCao?
四种情况
样例输出SampleOutput
WhereisJieCao?
数据范围及提示DataSize&Hint
四个数字的范围为1..100
题目思路IdeaOfProblem
本题非常简单,定义四个变量,然后读入那四个整数,根据相应的条件给出相应的结果。
样例程序ExampleCode
#include
usingnamespacestd;
main()
{
inta[4],i;
boolb=1;
for(i=0;i<=3;i++)
cin>>a[i];
if(a[0]==a[1]&&a[0]==a[2]&&a[0]==a[3])
{
cout<<"JieCaoConstant";
return0;
}
for(i=1;i<=3;i++)
if(a[i]<=a[i-1])
b=0;
if(b)
{
cout<<"JieCaoUp";
return0;
}
b=1;
for(i=1;i<=3;i++)
if(a[i]>=a[i-1])
b=0;
if(b)
cout<<"JieCaoDown";
else
cout<<"WhereisJieCao?
";
}
02RP增加定理
题目描述Description
SkyAo最近心血来潮的参考GPA运算公式发明了一个RP增加均值定理:
RP增加均值=Σ(某事件增量*RP评级)/增量和
比如:
扶老奶奶过马路增量为2,遵守交通规则增量为1,热心帮助同学增量为3。
参考考试制度,满分为100,如果SkyAo做该事件的得分在69分以上,就算RP增加成功,会获得该事件所设定的增量。
举个例子而言,如果SkyAo帮助老奶奶过马路,但老奶奶其实不想过马路,虽然他扶老奶奶很认真,但还是只能算得了60分,那么就无法获得评级(评级为0),但该事件增量还是2,如果SkyAo得分90,那么就会得到2RP增量,并且评级为4。
换句话说,事件增量只与事件本身有关,与此人表现无关。
评级在0-4之间,最高评级为4,最低为0。
于是SkyAo决定写个程序来快速计算自己最近的RP增量。
输入描述InputDescription
请你从输入中读入相关数据。
输入的第一行包括一个整数n(1≤n≤10),表示他最近所做的好事。
接下来的n行每行两个实数a(0≤a≤8)和b(0≤b≤4),表示SkyAo的某事件增量和RP评级。
样例输入SampleInput
10
23.7
03
53.7
04
33.3
34
14
24
24
23.5
输出描述OutPutDescription
输出只包括一个实数,请保留2位小数。
样例输出SampleOutput
3.74
题目思路IdeaOfProblem
这题就是计算GPA。
GPA大家都会算吧,把学分都加起来,然后统计学分和绩点之积的和。
两个数的商便是本题的解。
当然别忘了保留两位小数。
样例程序ExampleCode
#include
#include
#include
#include
#include
usingnamespacestd;
intn;
doublea,b;
intmain()
{
cin>>n;
doublet1=0.0,t2=0.0;
for(inti=1;i<=n;i++)
{
cin>>a>>b;
t1=t1+a;
t2+=a*b;
}
printf("%.2lf\n",t2/t1);
return0;
}
03BobWu与网游
题目描述Description
很多童鞋喜欢玩一些lol、dota这类游戏,这类游戏有一个特点,在你不死的情况下连续杀人会有不同称号。
完成游戏后,BobWu非常后悔,他总是说“少死一次就好了”。
下面为连续杀人次数对应的称号
0/1/2-YouareaFoolishMan
3-YouareonaKillingSpree
4-YouareDominating
5-YouhaveaMega-Kill
6-YouareUnstoppable
7-YouareWickedSick
8-YouhaveaM-m-m-m....MonsterKill
9-YouareGodlike
10+-YouareBeyondGodlike(Somebodykillhim!
)
输入一组字符,其中只包含K和D,K代表杀敌,D代表死亡,求他少死一次的情况下的最高称号。
输入描述InputDescription
输入一组字符,其中只包含K和D
输出描述OutputDescription
他少死一次的情况下的最高称号。
样例输入SampleInput
KKKDKKDDDKKKK
样例输出SampleOutput
YouhaveaMega-Kill
数据范围及提示DataSize&Hint
字符串长度小于20000
题目思路IdeaOfProblem
此题的关键在于求少死一次的最大击杀数。
我们可以将D作为分割符,将连续的K的个数记录在数组中,然后求出数组中相邻两位之和的最大值即可。
最后将对应的称呼进行输出。
样例程序ExampleCode
#include"stdio.h"
intmain()
{
chars[32767];
inti,j=0,ss=0,t[1000];
scanf("%s",s);
for(i=0;s[i]!
='\0';i++)
if(s[i]=='D')
{
t[j++]=ss;
ss=0;
}
else
ss++;
t[j++]=ss;
ss=0;
if(j==0)
ss=t[0];
else
for(i=0;iss=(t[i]+t[i+1])>ss?
(t[i]+t[i+1]):
ss;
switch(ss){
case0:
printf("YouareaFoolishMan");break;
case1:
printf("YouareaFoolishMan");break;
case2:
printf("YouareaFoolishMan");break;
case3:
printf("YouareonaKillingSpree");break;
case4:
printf("YouareDominating");break;
case5:
printf("YouhaveaMega-Kill");break;
case6:
printf("YouareUnstoppable");break;
case7:
printf("YouareWickedSick");break;
case8:
printf("YouhaveaM-m-m-m....MonsterKill");break;
case9:
printf("YouareGodlike");break;
default:
printf("YouareBeyondGodlike(Somebodykillhim!
)");
}
return0;
}
04Linkinpaoger的节操竞赛
题目描述Description
Linkinpaoger平时总喜欢刁难你,这次刁难完之后发现自己的节操全无,于是准备向上帝求助,希望能获得一些新节操,上帝告诉Linkinpaoger:
与你比赛一场,规则如下:
在一个房间里放置了若干堆节操,每次只能从某一堆中取走部分或者全部的节操(由你先取第一份),取到最后一份节操的人获胜。
在比赛前允许大家进入房间看看节操们。
如果Linkinpaoger赢了,就能免费获得房间里所有的节操。
于是Linkinpaoger来邀请你参加比赛,你不想浪费无意义的时间,所以去房间看了节操堆数和各堆的数目,据说,这样就能确定在最优情况下谁会赢。
那么……有了堆数和各堆数目,谁才会赢呢。
输入描述InputDescription
第一个值N为节操堆数,接下来输入N个数,每个数表示各堆节操数目n,N与n均为正整数
输出描述OutputDescription
赢得这次比赛的人:
Linkinpaoger或Me
样例输入SampleInput
211
样例输出SampleOutput
Linkinpaoger
数据范围及提示DataSize&Hint
无
题目思路IdeaOfProblem
如果仅仅是两堆石子,那么上述两个问题很好解决:
1〉当两堆石子数目相等的时候,当前局面为必败局面,否则为必胜局面,显然,两堆均为0颗是满足这个方法的;
2〉如果当前局面是必胜局面,那么从石子较多的那一堆里面取,使得两堆石子数相等,这样便转化到了必败局面。
然而,对多于两堆石子,1〉可以照旧,但是这样一来2〉远远没有这么简单,因为不太可能取后使得所有堆数目都一样(除非除了石子最多的一堆之外其它所有堆石子数目都相等)。
因此需要找一组更加有效的方法,想到的方法是这样的:
1〉把所有堆的石子数目用二进制数表示出来,当全部这些数按位异或结果为0时当前局面为必败局面,否则为必胜局面;
2〉(定理0)一组自然数中必然存在一个数,它大于等于其它所有数按位异或的结果。
因此在必胜局面下,因为所有数按位异或的结果是大于零的,那么通过一次取,将这个(大于其它所有数按位异或的结果的)数下降到其它所有数按位异或的结果,这时局面就变为必败局面了。
有了上述理论,此题就很简单了。
样例程序ExampleCode
#include"stdio.h"
longtemp[100];//房间的个数
intmain()
{
inti,n;
longmin;
scanf("%d",&n);
for(i=0;iscanf("%ld",&temp[i]);//第i个房间的节操数量
min=temp[0];
for(i=1;imin=min^temp[i];//按位异或
if(min==0)
printf("Linkinpaoger");//我输
else
printf("Me");//我赢
//printf("%ld",min);
return0;
}
05数学魔王
题目描述Description
Linkinpaoger做了个梦,梦中Anonymous被魔王拐走了,而他过五关斩六将的来到魔王的城堡,魔王告诉他,
他的城堡的城门需要用三个素数才能打开,魔王会给出一个大于5的整数,这三个素数的和是这个整数,
Linkinpaoger想到,可以用计算机来排列出所有可能的情况,Linkinpaoger想知道所有可能的情况。
输入描述InputDescription
一共一行,为一个大于5的正整数N
输出描述OutPutDescription
输出符合条件的前2000个素数组合,输出排列按如下顺序:
先比较第一个数的大小,第一个数小的先输出,如果相同,比较第二个数,第二个数小的先输出,最后比较最后一位。
样例输入SampleInput
10
样例输出SampleOutput
235
253
325
352
523
532
数据范围及提示DataSize&Hint
1<=N<=30000
因为要打印的内容较多,建议大家使用printf函数来输出,这样可以节省一些时间。
题目思路IdeaOfProblem
这题第一个问题便是怎么快速的生成素数?
这里我们推荐大家使用筛数法。
从2循环N,如果他不能被我们已经找到的素数整除的话,那么就可以认为它也是一个素数。
这样我们便可以以O(x)的复杂度解决这个问题,而且生成的素数序列是升序的。
(x为1..N的素数个数)
第一个问题解决后,那么下一个问题自然便自然的呈现在我们面前。
如何找出在上面生成的素数数列中找出和为定值N的三个素数?
这里其实有两种方法,一种是深度优先搜索加一定的剪枝,另外一种是把他看成背包问题。
这里我们受时间所限,就以深度优先搜索为例。
深度优先搜索本身的复杂度是O(2^N),对于这道题明显是不合适的,所以我们需要剪枝。
首先如果我们枚举的三个数的和已经大于那个定值,那么将搜索进行下去是没有意义的。
其次,要是前面两个数已经确定的情况下,如果已经找到第三个数的话,那么搜索也没有必要进行下去。
如果我们就用以上的思路去写程序,经过测试后一定会发现,较大的奇数可以通过时限,然而较大的偶数却不行。
这是因为奇数的情况数较少,只有奇数+奇数+奇数和奇数+偶数+偶数这两种情况。
前面那种情况很少出现,而后面那种情况只有一个解。
而偶数则不同,
有偶数+偶数+偶数,偶数+奇数+奇数两种情况,前面那种只能是2+2+2=6,后面是2+奇数+奇数。
奇数素数+奇数素数的情况相较之前的情况要多的多。
不过,值得庆幸的是,2必定是这三个素数中的一个。
这样我们便能降低深度优先搜索的深度,用标记法降低时间复杂度。
样例程序ExampleCode
#include
#include
#include
#definemaxn30005
boolvis[maxn];
intprime[maxn];
intnum,s,c;
boolselected[maxn]={0};
boolonce;
usingnamespacestd;
intClear()
{
memset(vis,0,maxn);
intnum=0;
vis[1]=1;
for(longi=2;i{
if(!
vis[i])
{
prime[++num]=i;
}
for(intj=1;j<=num&&i*prime[j]{
vis[prime[j]*i]=1;
if(i%prime[j]==0)
break;
}
}
returnnum;
}
booldfs_even(intdepth,intsum,int*nums)
{
if(depth==3)
{
if(sum==s)
{
selected[nums[0]]=true;
selected[nums[1]]=true;
selected[nums[2]]=true;
printf("%d%d%d\n",nums[0],nums[1],nums[2]);
c++;
returntrue;
}
else
{
returnfalse;
}
}
else
{
intnowsum=sum;
for(inti=1;i<=num;i++)
{
nowsum=sum+prime[i];
if(nowsum>s||c==2000)
{
break;
}
elseif((once&&selected[prime[i]]==false))
{
continue;
}
else
{
nums[depth]=prime[i];
if(dfs_even(depth+1,nowsum,nums))
break;
if(depth==0)
{
once=true;
}
}
}
}
returnfalse;
}
booldfs_odd(intdepth,intsum,int*nums)
{
if(depth==3)
{
if(sum==s)
{
printf("%d%d%d\n",nums[0],nums[1],nums[2]);
c++;
returntrue;
}
else
{
returnfalse;
}
}
else
{
intnowsum=sum;
for(inti=1;i<=num;i++)
{
nowsum=sum+prime[i];
if(nowsum>s||c==2000)
{
break;
}
else
{
nums[depth]=prime[i];
if(dfs_odd(depth+1,nowsum,nums))
{
break;
}
}
}
returnfalse;
}
}
intmain()
{
cin>>s;
num=Clear();
intss[3]={0};
c=0;
if(s%2!
=0)
dfs_odd(0,0,ss);
else
{
once=false;
dfs_even(0,0,ss);
}
return0;
}