HNU程序设计训练 斯诺克台球(屑题)

目录

题干

分析

代码


题干

“太长不看”

【问题描述】

斯诺克台球是一项古老而又时尚的运动,使用长方形球桌,台面四角以及两长边中心位置各有一个球袋,使用的球分为1个白球,15个红球和6个彩球共22个球。

其中母球(白球)1只,目标球21只。目标球中:红球15只各1分、黄球1只2分、绿球1只3分、咖啡球1只4分、蓝球1只5分、粉球1只6分、黑球1只7分。

选手需要使用球杆撞击母球去击打目标球来完成得分,每局开始时总是先从红球开始。击球顺序为先打进红球(每次击打允许多个红球同时落袋),然后必须任意指定一个目标彩球击打,如果该彩球被打进(打进后需要再摆回),然后接着击打红球,直到红球全部落袋,然后以黄、绿、咖啡、蓝、粉红、黑的顺序逐个击球(不再摆回),最后以得分高者为胜。任何时候红球落袋都不再摆回,任何时候因犯规导致彩球落袋,彩球必须摆回。

斯诺克比赛由双方轮流击打,必须击打合规的目标球,打进则本方得到相应的分数并继续击打,未打进或犯规轮换为对方击打,未打进不得分,犯规将进行罚分处理。

犯规规则如下:

1.     当击打目标球时,如果先击打到或同时击打到一个或多个其他颜色的球,或者有其他颜色的球落袋,或者打空(未击打到任何球),则视为犯规。此时需要比较目标球的分值和与本犯规相关的其他颜色的球的分值,取其中最高的分值,如果该分值小于4,则对方加4分,否则对方加该分值。

2.     当击打红球落袋后,继续击打任意彩球时打空,即未打击到任何球,对方加4分。

相比正式的斯诺克比赛,本问题对规则进行了简化,任何时候都可以结束比赛并计算比赛结果,不考虑白球落袋的情况。

信息化时代的智能台球桌能自动记录实际比赛时的击打记录,并传送到后台,但该记录仅仅是流水记录,并且无参赛选手的任何信息,需要你编程计算每场比赛的比分,同时需要计算单杆100分及以上的情况(单杆得分是指选手一次连续击打所得分数之和)。

【输入形式】

输入第一行为正整数t (t≤100),表示有t组测试数据,每组数据代表一局比赛。

在输入中,球的颜色表示为:

               r-红色球 y-黄色球 g-绿色球 c-咖啡色球 b-蓝色球 p-粉红球 B-黑色球

接下来的每组数据包括若干行,每一行为一次击打的结果,为智能球桌记录下来的流水记录,每组数据最后一行为-1,表示每组数据的结束。

流水记录包含用空格分隔的2个部分:

首先撞到的球 落袋球及数量

第一部分“首先撞到的球”为一个字符串,可以是“rygcbpB”中1个或多个字符组合(可能有多个字符“r”),或为字符串“NULL”。为“NULL”时,第二部分必为空,表示该次击打未撞击到任何球也没有任何球落袋。当红球落袋后继续击打任意彩球时,该部分为“ygcbpB”中的任意单个字符时都认为是合规的目标球。

第二部分“落袋球及数量”为一个字符串,例如“r2gb”,代表本次击打有两个红球落袋,以及绿球和篮球落袋,红色球r后面有数字(大于0小于16),表示红球的落袋数,其他彩球后无数字。该部分可以为空,表示本次击打无球落袋。

比赛在A与B之间进行,每局比赛总是由A先开球。

【输出形式】

输出为t+1行,前t行每行输出用冒号分隔的两个整数,表示每局比赛A与B之间的比分;最后一行输出用冒号分隔的两个整数,表示t局比赛之后A与B之间获得的单杆100分及以上的次数之比(单杆得分是指选手一次连续击打所得分数之和)。

【样例输入】

3
r r1
B
r r2
c c
r r1
b g
-1
rp r1
r br2B
NULL
r r12
y y
g p
-1
rr r3
NULL
r r1
yg y
-1

【样例输出】

6:7
13:24
7:5
0:0

【样例说明】

第一局比赛:

A击打红球,打进1个红球,得1分,比分为 1:0

A继续击打任意彩球,打到黑球,未打进,不得分,比分为1:0

轮换为B击打红球,打进两个红球,得2分,比分为1:2

B继续击打任意彩球,打到咖啡球,打进咖啡球,咖啡球摆回,得4分,比分为1:6

B继续击打红球,打进一个红球,得1分,比分为1:7

B继续击打任意彩球,打到蓝球,打进绿球,犯规,取分值最大者蓝球,绿球摆回,对方加5分,比分为6:7

-1比赛结束

第二局比赛:

A击打红球,首先打到红球和粉球,犯规,打进1个红球和咖啡球,犯规,咖啡球摆回,取分值最大的粉球,对方加6分,比分为0:6

B击打红球,首先打到红球,打进蓝球、2个红球和黑球,犯规,蓝球和黑球摆回,取分值最大的黑球,对方加7分,比分为7:6

A击打红球,未打到任何球,犯规,对方加4分,比分为7:10

B击打红球,打到红球,打进12个红球,加12分,比分为7:22

B击打任意彩球,打到黄球,打进黄球,黄球摆回,得2分,比分为7:24

B击打黄球,打到绿球,打进粉球,犯规,粉球摆回,对方加6分,比分为13:24

-1比赛结束

第三局比赛:

A击打红球,打到2个红球,打进3个红球,加3分,比分为3:0

A击打任意彩球,打空,未打到任何球,对方加4分,比分为3:4

B击打红球,打到1个红球,打进1个红球,加1分,比分为3:5

B击打任意彩球,打到黄球和绿球,打进黄球,犯规,黄球摆回,取分值最高的绿球,绿球分值小于4,对方加4分,比分为7:5 

-1比赛结束

3局比赛中无人单杆得分过100,最后一行输出0:0

分析

第一次气到发CSDN,分N多类讨论的屑题。

明确一件事,一次击球分三种情况:①犯规 ②击球未进,交换击球 ③击球得分

首先分为红球未清空和红球清空两种情况。

红球未清空时,犯规的情况如下:

Ⅰ击中的不是目标球(可区分为击中一种和多种)

目标球可区分为红球和彩球。

会出现重复击球(连续打两次红球/彩球)的情况,也算犯规。

Ⅱ没有击中任何球

Ⅲ打进的球不是目标球(可区分为打进一种和多种)

红球清空后,犯规的情况如下:(此时需要考虑目标球是否大于4分)

Ⅰ击中的不是目标球

Ⅱ未击中任何球

Ⅲ打进的球不是目标球

需要做的事:

1.红球未清空时,记录剩下多少红球

2.红球清空后,记录目标球

3.红球未清空时,记录上一目标球是红球还是彩球

4.记录选手的分数和单杆得分

5.当单杆得分达到100时记录

6.犯规时交换选手击球顺序

容易错的点:

1.当同时打进多种球,此时已经造成了犯规,但是已经打进的红球依然会消耗,即不会再返场。记录剩余红球时,这一部分也要减掉。

2.当选手一杆打空场上红球且得分有效,需要注意的是接下来一杆,目标为彩球,但不适用红球打完的规则。即该情况下目标球可以是任意彩球,打进以后彩球仍然可以返场。

3.红球未清空时,击球交换顺序后,选手的目标球一定是红球。

最后,单杆100分的定义很模糊,并没有说对手犯规的分数会不会算在里边。经测试是加不加都不影响结果的。但是我个人认为不算该选手的单杆得分。

代码(281行,大佬们可以试试精简)


#include<iostream>
using namespace std;
struct billiards{
	int sc;//得分 
	int conti;//连续得分 
	bool host;//是否正在击球 
	char end;//上一球 
	bool combo;//单杆100是否重复计算 
};

bool non_repeat_R(string &a){//字符串1(首次击中的球)中是否有除r以外其他的字符 
	for(int i=0;i<(int)a.size();i++)
	if(a[i]!='r') return 0;
	return 1;
}

void swap2(billiards *b){//交换击球顺序 
	if(b[0].host==1){
		b[0].host=0;
		b[0].conti=0;
		b[0].combo=0;
		b[1].host=1;
		b[0].end='c';
		b[1].end='c';
	}
	else{
		b[0].host=1;
		b[1].conti=0;
		b[1].combo=0;
		b[1].host=0;
		b[1].end='c';
		b[0].end='c';
	}
}
int findspace(string a){//找到两个字符串之间的空格 
	for(int i=0;i<(int)a.size();i++){
		if(a[i]==' ') return i; 
	} 
	return -1;
}

int findhost(billiards *b){//找到正在击球的人 
	if(b[0].host==1) return 0;
	return 1;
}

void punish(billiards *b,int i,string x,char &temp){//犯规惩罚 
	int max=4;
	if(temp=='b' && max<5) max=5;
		else if(temp=='p' && max<6) max=6;
		else if(temp=='B' && max<7) max=7;
	for(int k=0;k<(int)x.size();k++){
		if(x[k]=='b' && max<5) max=5;
		else if(x[k]=='p' && max<6) max=6;
		else if(x[k]=='B' && max<7) max=7;
	}
	swap2(b);
	b[findhost(b)].sc+=max;
}

void goalc(billiards *b,char &c,int i){//彩球得分 
	switch(c){
				case 'y':{
					b[i].conti+=2;
					b[i].sc+=2;
					break;
				}
				case 'g':{
					b[i].conti+=3;
					b[i].sc+=3;
					break;
				}
				case 'c':{
					b[i].conti+=4;
					b[i].sc+=4;
					break;
				}
				case 'b':{
					b[i].conti+=5;
					b[i].sc+=5;
					break;
				}
				case 'p':{
					b[i].conti+=6;
					b[i].sc+=6;
					break;
				}
				case 'B':{
					b[i].conti+=7;
					b[i].sc+=7;
					break;
				}
			}
	b[i].end='c';		
}

void decreaser(string &a,int &remain){//红球入袋后不再返场 
	int count=0;
	for(int i=0;i<(int)a.size();i++){
		if(a[i]>='0' && a[i]<='9'){
		count+=(a[i]-48);
		count*=10;	
		}
	}
	count/=10;
	remain-=count;
}

void goalr(billiards *b,string &a,int k,int &remain){//红球得分 
	int count=0;
	for(int i=1;i<(int)a.size();i++){
		count+=(a[i]-48);
		count*=10;
	} 
	count/=10;
	b[k].conti+=count;
	b[k].sc+=count;
	b[k].end='r';
	remain-=count;
}

void emptyred(billiards *b,int &i,char &temp){//红球空之后击中目标彩球 
	switch(temp){
	    case'y':{
	    	b[i].sc+=2;
	    	b[i].conti+=2;
	    	temp='g';
			break;
		}
		case'g':{
	    	b[i].sc+=3;
	    	b[i].conti+=3;
	    	temp='c';
			break;
		}
		case'c':{
	    	b[i].sc+=4;
	    	b[i].conti+=4;
	    	temp='b';
			break;
		}
		case'b':{
	    	b[i].sc+=5;
	    	b[i].conti+=5;
	    	temp='p';
			break;
		}
		case'p':{
	    	b[i].sc+=6;
	    	b[i].conti+=6;
	    	temp='B';
			break;
		}
		case'B':{
	    	b[i].sc+=7;
	    	b[i].conti+=7;
			break;
		}
	}
}

void rulestrike(billiards *b,int i,string &x,int &remain,char &temp){//按规则判断 
	int space=findspace(x);
	if(remain>0 || (remain==0 && b[i].end=='r')){//红球未清空,或清空红球后最后一次击彩球 
		if(space==-1){//未打进任何一球 
			int count=0;
	        for(int k=0;k<(int)x.size();k++)
	        if(x[k]=='r') count++;
	        if(count==(int)x.size()) x="r";//同时击中多个红球等于一个 
	        if(x.length()>1) punish(b,i,x,temp);//同时击中不同种,犯规 
	        else if(x.length()==1){
		        if(b[i].end=='r'){
			    if(x=="r") punish(b,i,x,temp);//重复击球,犯规 
			    else swap2(b);//未击中,交换击球顺序 
		}
		        else if(b[i].end=='c'){//同上 
		    	if(x!="r") punish(b,i,x,temp);
			    else swap2(b);
		}
}
		}
	else if(b[i].end=='r'){//上一球进红球,这一球目标彩球 
		if(remain==0) b[i].end='c';
		string tmp=x.substr(0,space),tmp2=x.substr(space+1,x.length());
	    if(tmp.length()>1 || tmp2.length()>1){//击球超过一种,犯规 
	    	punish(b,i,x,temp);
		    decreaser(tmp2,remain);//红球入袋不返场 
		}
		else if(x[0]=='r'){//重复击球,犯规 
			punish(b,i,x,temp);
		}
		else{
			if(tmp!=tmp2) punish(b,i,x,temp);//击球超过一种,犯规 
			else goalc(b,x[0],i);//击球符合规则 
		    }
		}
    	else if(b[i].end=='c' && remain!=0){//上一球进彩球,这一球目标红球 
		string tmp=x.substr(0,space),tmp2=x.substr(space+1,x.length());
		if(!non_repeat_R(tmp)) {//击球超过一种,犯规 
			decreaser(tmp2,remain);
			punish(b,i,x,temp);
		}
		else{
			int count=0;
			for(int k=0;k<(int)tmp2.size();k++)
			    if((tmp2[k]>'a' && tmp2[k]<'z' && tmp2[k]!='r')||tmp2[k]=='B'){
			    	count++;
			    	break;
				}
			if(count>0){//击球超过一种,犯规 
				punish(b,i,x,temp);
				decreaser(tmp2,remain);
			}
			else goalr(b,tmp2,i,remain);//击球符合规则 
		} 
	}
    }
	else{//红球已经清空,开始打彩球 
		if(space==-1 && x.length()>1) punish(b,i,x,temp);//击球超过一种,犯规 
	    else if(space==-1 && x.length()==1){
	    	if(x[0]==temp) swap2(b);//击球不进,交换击球顺序 
	    	else punish(b,i,x,temp);//未击中目标球,犯规 
		}
		else{
			string tmp=x.substr(0,space),tmp2=x.substr(space+1,x.length());	
			if(tmp.length()>1 || tmp2.length()>1) punish(b,i,x,temp);//击球超过一种,犯规 
			else{
				if(tmp!=tmp2) punish(b,i,x,temp);//击球超过一种,犯规
				else if(tmp[0]!=temp) punish(b,i,x,temp);//未击中目标球,犯规 
				else emptyred(b,i,temp);//击球符合规则 
			}
		}
	}	
}

int main(){
	int n;
	cin>>n;
	cin.get();
	int counta=0,countb=0;//记录单杆100分的次数 
	while(n--){
		int remain=15;//初始15个红球 
		char temp='y';
		billiards b[2];//b[0]->A,b[1]->B 
		b[0].host=1;//A先击球 
		b[0].combo=0;
		b[1].combo=0;
		b[1].host=0;
		for(int i=0;i<2;i++){
			b[i].conti=0;
		    b[i].sc=0;
		    b[i].end='c';
		}//初始化完毕 
		string a;
		while(getline(cin,a),a!="-1"){
			if(a=="NULL"){//未击中,犯规 
				if(remain==0)
				punish(b,findhost(b),a,temp);
				else{
					swap2(b);
					b[findhost(b)].sc+=4;
				}
			}
			else{//记录为击中 
				rulestrike(b,findhost(b),a,remain,temp);//按规则判定 
				if(b[findhost(b)].conti>=100 &&b[findhost(b)].combo==0){ 
					if(findhost(b)==0){//单杆过100分且未被记录,则记录 
						 counta++;
						 b[0].combo=1;
					}
					else{
						countb++;
						b[1].combo=1;
					}
				}
			}
		}
	cout<<b[0].sc<<':'<<b[1].sc<<endl;//输出得分 
	}
	cout<<counta<<':'<<countb<<endl;//输出单杆100次数 
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值