2021-08-21

蓝桥青少编程C++高级组2021模拟八题解题思路辨析

8.(编程题)
编程实现:石头剪刀布
程序命名:game.cpp
题目描述:
放假期间,小蓝与电脑对垒,玩起了一款经典的游戏:“石头剪刀布”。游戏规则想必大家已经非常熟悉了:两边一样则为平局,否则石头胜于剪刀;剪刀胜于布;布胜于石头。小蓝与电脑的对垒一共有n个回合,平局或败局得分为0;胜局得分取决于小蓝出手的阵容,剪刀、石头、布各有不同的分值:
出手"石头”赢的话得r点分值;
出手“剪刀”赢的话得s点分值;
出手"布"赢的话得c点分值;
但是,在第i回合中,小蓝不能使用在第(i-k)个回合中使用的阵容。(在前k个回合中,小蓝可以使用任何阵容。)
在游戏开始之前,电脑已经事先安排好了每回合比赛的阵容,而小蓝居然未卜先知了电脑的阵容!电脑的出手阵容用字符串t给出,如果第个字符(1<=i<=n)为r,则代表电脑将在第i个回合中出手"石头"。同样,c和s分别代表“布"和“剪刀”。
那么请你计算一下,小蓝在游戏中可以获得的最大分值是多少?
输入:(其中:n,k,r,s,c都是整数,t是字符串。2<=n<=20 1<=k<=n-1 1<=r,s,c<=1000 字符串t的长度是n)
n k
r s c
t
输出:
小蓝在游戏中可以获得的最大分值。
样例输入:
5 2
8 7 6
rsrcr
样例输出:
27

样例说明:
机器出手的阵容是:石头、剪刀、石头、布、石头
则小蓝出手:布、石头、石头、剪刀、布,分值为6+8+0+7+6=27分
第3回合里,小蓝不能再出第(3-2=1)回合里出过的“布”了,所以选择了平局,出手“石头”,得0分。

第八题参考代码的思路有错误,因为与i与(i-k)重复而改为平局,却可能影响后面(i+k):
可以输入:10 3 8 7 6 rrrrsssccc
输出为48,是错的:str2为:cccrrrsssc 而正确策略为:cccsrrrsss

//石头剪刀布 (思路有错误)
#include <iostream>
using namespace std;
char str1[22],str2[22]; 
int main(int argc, char *argv[])
{
	int n,k,r,s,c;
	cin>>n>>k>>r>>s>>c>>str1;
	int sum=0;
	for(int i=0;i<k;i++){
		if(str1[i]=='r'){
			str2[i]='c';sum+=c;
		}
		else if(str1[i]=='s'){
			str2[i]='r';sum+=r;
		}
		else if(str1[i]=='c'){
			str2[i]='s';sum+=s;
		}	
	}
	for(int i=k;i<n;i++){
		if(str1[i]=='r'){
			if(str2[i-k]=='c')	str2[i]='r';
			else {
				str2[i]='c';sum+=c;
			}	
		}
		else if(str1[i]=='s'){
			if(str2[i-k]=='r')	str2[i]='s';
			else {
				str2[i]='r';sum+=r;
			}
		}
		else if(str1[i]=='c'){
			if(str2[i-k]=='s')	str2[i]='c';
			else {
				str2[i]='s';sum+=s;
			}	
		}	
	}
	cout<<sum<<endl;
	return 0;
}

版权声明:此题及代码应用为CSDN博主「lybc2019」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/lybc2019/article/details/114977574

此题我第一能想到的是枚举法,代码如下。但枚举法太笨,效率太低,大家还有更好的吗?

#include <bits/stdc++.h>
using namespace std;
//枚举只含'r'、's'、'c'的n位字串,模仿3进制
void go(int n,char str[],int i)
{
  if(i>=n)return;	//递归出口
  if(str[i]=='r')str[i]='s';
  else if(str[i]=='s')str[i]='c';
  else if(str[i]=='c')
  {
    str[i]='r';
    //if(i<(n-1))
    go(n,str,i+1);
  }
}
//根据“剪刀石头布”游戏规则,比较两个出招队列,后一个队列的得分
int compare(int n,char t1[],char t2[],int k,int r,int s,int c)
{
  int sum=0;
  for(int i=0; i<n; i++)
  {
    if(i-k>=0)if(t2[i-k]==t2[i]) return 0;
    if(t1[i]=='r'&&t2[i]=='c')sum+=c;
    if(t1[i]=='s'&&t2[i]=='r')sum+=r;
    if(t1[i]=='c'&&t2[i]=='s')sum+=s;
  }
  return sum;
}
int main()
{
  int n,k;			//n比赛回合数,
  char t1[22],t2[22],t[22];	//t1为电脑出招队列,'r'、's'、'c'为出石剪布
  int r,s,c;		// r、s、c为出石、剪、布赢了的得分
  int sum=0,max=0;		//总分,max总分最大值(擂台法)
  cin>>n>>k>>r>>s>>c>>t1;
//枚举法:队列从"rrr…"开始逐一枚举到"ccc…" ,t为最终队列
  for(int i=0; i<n; i++)
  {
    t2[i]='r';
    t[i]='c';
  }
  t2[n]='\0';
  t[n]='\0';
  while(strcmp(t2,t))
  {
    go(n,t2,0);
    sum=compare(n,t1,t2,k,r,s,c);
    //if(sum>20) cout<<t2<<"  "<<sum<<endl;
    if(sum>max)max=sum;
  }
  cout<<max<<endl;
  return 0;
}

此题不用暴力搜索应怎么解决呢?我我们还是从最开始分析一下吧:

1. 问题分析:

第i个与i-k如果相同,就需其中一个,比如将第i个改为平或者输。但如果改动i为平局,可能与后面的i+k赢局相同,迫使i+k也改为平局,这种情况可以将第i个改为输局。

2. 数学建模:

n:长度,k不能相同的间隔数,r,s,c:石、剪、布赢了的的分值, sum:总分; str1[i]:电脑的出招序列,str2[i]:小蓝的出招序列

3. 算法设计:

基本算法思想:递推+记忆化
具体算法设计:对照str1从左到右依次确定小蓝的出招,规则是先按照“赢”来出招,再判断是否与前k位相同(递推)。如果相同且前k位是赢局,就将当前改动为平局,如果前k位是平局,就当前继续为赢,只将前K位重新改为输局。流程图(略)

4. 编程

#include <bits/stdc++.h>
using namespace std;

int r,s,c; //用于记录分别出石头剪刀布赢了的得分

//根据出招得到赢的出招 
char winChar(char ch)
{
  char ch2;
  if(ch=='r') ch2='c';
  else if(ch=='s') ch2='r';
  else if(ch=='c')ch2='s';
  return ch2;
}
//根据出什么招赢的获得得分 
int winScore(char ch)
{
  int score=0;
  if(ch=='c') score=c;
  else if(ch=='s') score=s;
  else if(ch=='r') score=r;
  return score;
}
int main()
{
  char t1[22],t2[22];	//t1为电脑出招队列,
  int tt[22]= {0};		//记忆化用
  int n,k;			//n比赛回合数,
  int sum=0;		//总分
  cin>>n>>k>>r>>s>>c;

  cin>>t1;

  for(int i=0; i<n; i++)
  {
    t2[i]=winChar(t1[i]);
    int j=winScore(t2[i]);
    if(i>=k&&t2[i-k]==t2[i])//符合间隔k相同出招,要改动t2[i]
    {
      if(tt[i-k]==1)t2[i-k]=winChar(winChar(t2[i-k]));//将t1[i-k]原来的平局重新改为输局
      else
      {
        t2[i]=t1[i];//将t2[i]改为平局
        tt[i]=1;	//平局“记忆化 ”
        j=0;
      }
    }
    sum+=j;
  }
  cout<<sum<<endl;

  return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值