7779 - KKT基本算法304保龄球340

目录

  • 题目
  • 题解
    • 思路
      • 规则解析
      • “奠基”
      • 选择思路
      • 读取数据
      • 处理数据
    • 完整代码
  • 写在后面

题目

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),那么你应该成功了
另外再次抱怨一下这题目描述= =浪费了我多少宝贵的时间

你可能感兴趣的:(学校OJ题解)