蓝桥青少编程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;
}