7779 - KKT基本算法304保龄球340 |
---|
难度级别:A; 运行时间限制:1000ms; 运行空间限制:51200KB; 代码长度限制:2000000B |
试题描述 |
保龄球游戏,打保龄球是用一个滚球去打击十个站立的柱,将柱击倒,一局分十轮,每轮可以滚球一次或两次,以击倒的柱数为依据计分,一局得分为十轮得分之和,而每轮的得分不仅与本轮的滚球情况有关,还可能与后续一两轮的滚球情况有关。即某轮某次滚球击倒的柱数不仅要计入本轮得分,还可能计入前一两轮的得分,具体的滚球计柱规则和计分方法如下: (1)某一轮的第一次滚球就击倒全部十个柱,则本轮不再滚球(若是第十轮,则还需另加两次滚球,不妨称其为第十一轮和第十二轮,并不是所有的情况都需要滚第十一轮和第十二轮),该轮得分为本次击倒的柱数10,与以后两次滚球所击倒的柱数之和。 (2)如某一轮的第一次滚球未击倒十个柱,则可对剩下未击倒的柱再滚一次。如果这两次滚球击倒全部十个柱,则本轮不再滚球(若是第十轮则还需另加一次滚球),该轮得分为这两次共击倒柱数10与以后一次滚球所击倒柱数之和。 (3)若某一轮两次滚球未击倒全部十个柱,则本轮不再继续滚球,该轮得分为这两次滚球击倒的柱数之和。总之,若某一轮中一次滚球或两次滚球击倒十个柱,则本轮得分是本轮首次滚球开始的连续三次滚球击倒柱数之和(其中有一次或两次不是本轮滚球)。若一轮内二次滚球击倒柱数不足十个,则本轮得分即为这两次击倒柱数之和。下面以实例说明如下: 见下方附表 现在请编写一个保龄球计分程序,用来计算并输出最后的总分。 |
输入 |
输入一行,为前若干轮的滚球结果,每轮滚球结果用一到两个字符表示,每一个字符表示一次击球,各轮结果之间用一个空格分隔。字符“/”表示击倒当前球道上的全部的柱,否则用一个数字字符表示本次滚球击倒的当前球道上的柱的数目,两轮滚球之间用一个空格隔开。 |
输出 |
输出一行一个整数,代表最后的得分。 |
输入示例 |
90 90 / 9/ 81 / / / 72 / / 0 |
输出示例 |
170 |
附表
轮 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
击球情况 | / | / | / | 72 | 9/ | 81 | 8/ | / | 9/ | / | 8/ | |
各轮得分 | 30 | 27 | 19 | 9 | 18 | 9 | 20 | 20 | 20 | 20 | ||
累计总分 | 30 | 57 | 76 | 85 | 103 | 112 | 132 | 152 | 172 | 192 |
注意这道题的题目有些难以理解,请务必看懂再自己做或读下去
(1)我们管一次滚球就击中全部十个瓶叫做“全中”,记为“/”;而第二次滚球才击中全部十个瓶叫做“补中”,记为“(第一次击球数)/”,比如“3/”;如果两次都没有击中全部的球,则记为“(第一次击球数)(第二次击球数)”,比如“72”。
(2)比赛有十轮,每一轮有两次击球机会(非常重要,题目中的描述不太严谨,“第十一轮”“第十二轮”的称呼并不对)
(3)在每一轮中,若第一次击球便全中,本轮得分为本轮得分10+奖励分数(后两次击球所得的分);若补中,本轮得分为本轮得分10+奖励分数(后一次击球所得的分);若两次并没有击中全部的球,本轮得分为第一次击球数+第二次击球数。
(4)对题目难以理解的地方的特别解释:1.因为每一轮全中或补中都需要有奖励分数,因此第十轮(最后一轮)若全中,为了获得奖励分数需要再滚两次球(而不是两轮),若补中,为了获得奖励分数需要再滚一次球(而不是一轮)2.补中时,本轮得分(不含奖励分数)为10,而不是第一次击球数+“/”所代表的10。
往下看之前,请务必自己思考一下,看看有没有与文中不同的新思路。
在做这道题的时候,我以及我的同学主要用到以下方法:
1.使用课课通上的方法,将每一轮得分存入一个字符串(总共是一个字符串数组),通过字符串“.size()”的特性以及字符判断进行分数计算
2.将每轮得分存入表中,最后计算总分时对每轮分数求和
3.将全部得分存入一个字符数组中,用while循环以及数组下标还有无数if-else判断得分情况,并累计计算总分。
4.将全部得分存入一个字符数组中,设计函数1:可以计算字符数组中某一区间的分数,函数2:根据条件确定区间。在每一轮判断全中或补中或普通情况,若全中,则将区间从本轮向后扩展两个有效数值(两次滚球),若补中,则将区间从本轮向后拓展一个有效数值。
最开始,我选择了思路3,但是因为这道题的数据有些奇怪,比如本应是“90 90 / 9/ 81 / / / 72 / / 0”却不知怎样少了个空格“90 90 / 9/ 81 / / / 72 / /0”,于是这种严格基于空格数计算下标的方法就泡汤了。
我百思不得其解,遂看同学代码,发现他使用了思路2,可惜我并不能读懂(因此对其解释最少),之后,我又尝试自己写出思路1的代码,可惜结果错误还是跃然于屏幕之上。
最终,我想到了思路4。本篇博客将主要讲解思路4。
读取数据时,我们需要将整行包括空格原封不动地存入字符数组,显然cin>>等方法就失效了(它们遇到空格就会结束输入)。
于是我从同学那里学来了一个高超的技术:
int p=0;
while(scanf("%c",&score[p])!=EOF)p++;
也就是说,这种输入方法不撞EOF(文件末尾)便不停下,尤其适合我们的需求。
PS:我们在cmd窗口输入时需要按Ctrl + Z结束输入才会得到后续输出
1.计算区间总分的函数
int calc(int f,int l)//f和l为区间的开头和结尾
{
int res=0;//result = =
for(int i=f;i<=l;i++)
{
//首先我们跳过空格字符
//"/"的ACSII码为47,减去'0'刚好的-1,,小于0,我们可以通过这个来判断
if(score[i]!=' ')res+=score[i]-'0'<0?10:score[i]-'0';//这里用到了三目运算符,可以百度获得使用方法,当然平时不建议使用
if(score[i]=='/' && score[i-1]!=' ' && i!=0)res=res-score[i-1]+'0';//这里是针对补中时也将“/”计算为10的bug的补丁,可能会有更好的方法
}
return res;
}
2.确定区间
int move(int f,int t)//f为区间开头,t为需要加入的滚球数
{
while(score[f]!=' ')f++;//跳过本轮的得分,进入下一轮
while(t)//当t!=0时,这里天然成立
{
f++;
if(score[f]!=' ')t--;//多一次滚球我们就离目标更近一些
}
return f;
}
3.判断部分
while(rd<=10)
{
if(score[i]=='/')sum+=calc(i,move(i,2)),i+=2;//若全中,则计算本轮和后两次滚球的得分计入总分 i+=2自动跳到下一轮
else if(score[i+1]=='/')sum+=calc(i,move(i,1)),i+=3;//若补中,则计算本轮和后一次滚球的得分计入总分 i+=3自动跳到下一轮
else sum+=calc(i,i+1),i+=3;//普通情况,则将两次击球数加起来计入总分 i+=3进入下一轮
rd++;//只能打10轮啊
}
#include
using namespace std;
char score[101];
int move(int f,int t)
{
while(score[f]!=' ')f++;
while(t)
{
f++;
if(score[f]!=' ')t--;
}
return f;
}
int calc(int f,int l)
{
int res=0;
for(int i=f;i<=l;i++)
{
if(score[i]!=' ')res+=score[i]-'0'<0?10:score[i]-'0';
if(score[i]=='/' && score[i-1]!=' ' && i!=0)res=res-score[i-1]+'0';
}
return res;
}
int main()
{
int i=0,p=0,sum=0,rd=1;
while(scanf("%c",&score[p])!=EOF)p++;
while(rd<=10)
{
if(score[i]=='/')sum+=calc(i,move(i,2)),i+=2;
else if(score[i+1]=='/')sum+=calc(i,move(i,1)),i+=3;
else sum+=calc(i,i+1),i+=3;
rd++;
}
cout<<sum;
return 0;
}
如果你能通过“90 90 / 9/ 81 / / / 72 / /0”(不使用针对样例的特判)(结果为170),那么你应该成功了
另外再次抱怨一下这题目描述= =浪费了我多少宝贵的时间