蓝桥寒假训练2->2013年第四届蓝桥杯国赛

来源:互联网 发布:项目资金管理系统 php 编辑:程序博客网 时间:2024/06/10 14:50

1.猜灯谜

A 村的元宵节灯会上有一迷题:    请猜谜 * 请猜谜 = 请边赏灯边猜小明想,一定是每个汉字代表一个数字,不同的汉字代表不同的数字。请你用计算机按小明的思路算一下,然后提交“请猜谜”三个字所代表的整数即可。

答案:897

法一:逐一枚举所有3位数,分离各位数字,判断与商中相应数字是否相同,且要判断商中十位与万位是否相同

#include <cstdio>#include <iostream>  using namespace std;  int main(){      int a,b,c,num;      for(int i=100;i<1000;i++){//请猜谜        if(i*i>=100000){            num=i*i;//请边赏灯边猜            a=i/100;            b=(i%100)/10;            c=i%10;            if(num/100000==a&&num%10==b&&(num%100)/10==(num/10000)%10){                cout<<i<<endl;            }        }    }    return 0;}  

法二:一共出现了6个不同的数字,还可用6重循环暴力破解出答案

2.连续奇数和

小明看到一本书上写着:任何数字的立方都可以表示为连续奇数的和。比如:
2^3 = 8 = 3 + 53^3 = 27 = 7 + 9 + 114^3 = 64 = 1 + 3 + ... + 15
虽然他没有想出怎么证明,但他想通过计算机进行验证。请你帮助小明写出 111 的立方之连续奇数和表示法的起始数字。如果有多个表示方案,选择起始数字小的方案。 


法一:枚举首项和尾项,等差数列求和

#include <cstdio>#include <iostream>using namespace std;int main(){int a=111*111*111;//1367631for(int i=1;i<=a;i+=2){//首项for(int j=i+2;j<=a;j+=2){//尾项int n=(j-i)/2+1;//项数int sum=n*(i+j)/2;//等差数列求和if(a==sum){cout<<i<<endl;return 0;}}}}

法二:奇数的等差序列an=2*n-1,Sn=n*n,也可以枚举首项和尾项为第几项

#include <cstdio>#include <iostream>using namespace std;int main(){ int a=111*111*111;    for(int i=1;i<=2000;i++){//首项项数    for(int j=i;j<=2000;j++){//尾项项数    if(j*j-(i-1)*(i-1)==a){    cout<<2*i-1<<endl;    }    }    }    return 0;}  


3.空白格式化

本次大赛采用了全自动机器测评系统。如果你的答案与标准答案相差了一个空格,很可能无法得分,所以要加倍谨慎!但也不必过于惊慌。因为在有些情况下,测评系统会把你的答案进行“空白格式化”。其具体做法是:去掉所有首尾空白;中间的多个空白替换为一个空格。所谓空白指的是:空格、制表符、回车符。


void f(char* from, char* to){char* p_from = from;char* p_to = to; while(*p_from==' '||*p_from=='\t'||*p_from=='\n') p_from++; do{if(*p_from==' '||*p_from=='\t'||*p_from=='\n'){ do{p_from++;}while(*p_from==' '||*p_from=='\t'||*p_from=='\n');    if(*p_from!='\0')//填空        *p_to++=' ';        }    }while(*p_to++=*p_from++);}//*p_from!='\0'

填空部分的意思,中间的多个空白替换为一个空格,并要判断是否到达’\0’,到达’\0’就不用再添加空格

4.高僧斗法

古时丧葬活动中经常请高僧做法事。仪式结束后,有时会有“高僧斗法”的趣味节目,以舒缓压抑的气氛。

节目大略步骤为:先用粮食(一般是稻米)在地上“画”出若干级台阶(表示N级浮屠)。又有若干小和尚随机地“站”在某个台阶上。最高一级台阶必须站人,其它任意。(如图1所示)

两位参加游戏的法师分别指挥某个小和尚向上走任意多级的台阶,但会被站在高级台阶上的小和尚阻挡,不能越过。两个小和尚也不能站在同一台阶,也不能向低级台阶移动。

两法师轮流发出指令,最后所有小和尚必然会都挤在高段台阶,再也不能向上移动。轮到哪个法师指挥时无法继续移动,则游戏结束,该法师认输。

对于已知的台阶数和小和尚的分布位置,请你计算先发指令的法师该如何决策才能保证胜出。

输入数据为一行用空格分开的N个整数,表示小和尚的位置。台阶序号从1算起,所以最后一个小和尚的位置即是台阶的总数。(N<100, 台阶总数<1000)

输出为一行用空格分开的两个整数: A B, 表示把A位置的小和尚移动到B位置。若有多个解,输出A值较小的解,若无解则输出-1。

例如:

用户输入:

1 5 9

则程序输出:

1 4

再如:

用户输入:

1 5 8 10

则程序输出:

1 3


法一:

以两个物体间距离为石子数来进行Nim博弈。从低阶到高阶每次取两个小和尚,和尚不重复取,他们中间的间隔即为一堆石子的数量,不过需要注意的是,这里的石子数能减少,也能增加。由博弈论Nim相关知识,若(a1^a2^a3^…..^an)!=0,一定存在着合法的移动,将ai变成aii后满足,(a1^a2^..^aii^…^an)=0 ,同时这一性质也是解决移动的方法,只要找出对一个石子堆能通过增加石子或者减少石子符合这种要求,就是一种解法。所以移动有两种情况:

一个是缩短距离,就是说两个物体间的距离减小后会到达P态(必败态)

另一个是增大距离,也就是右边的物体在允许的情况下向右移动一定位置后到达P态(必败态)

#include <cstdio>#include <iostream>using namespace std;int main(){int count=0,top=0,sum=0;int a[105],b[105];//读取小和尚位置while(scanf("%d",&a[count++])!=EOF);count--;//构建"石子堆",每次取两个小和尚的位置。若和尚数为奇数个,则最后一个多出的小和尚一定位于最高级台阶且不能移动,所以对移动没影响,可以不用理会for(int i=0;i+1<count;i+=2){b[top++]=a[i+1]-a[i]-1;}for(int i=0;i<top;i++){sum^=b[i];}if(!sum){cout<<-1<<endl;return 0;}//b[i]能与sum异或变小,说明可以通过减少此堆石子使N态转化为P态//b[i]能与sum异或变大且b[i]变大在下一石子堆允许的情况下,说明可以通过增加此堆石子使N态转化为P态for(int i=0;i<top;i++){//找第一个能使N态转化为P态的石子堆if((sum^b[i])<=b[i]){//减少石子printf("%d %d\n",a[i*2],a[i*2]+b[i]-(sum^b[i]));break;}else if((sum^b[i])>b[i]&&a[i*2+1]+(sum^b[i])-b[i]<a[i*2+2]){//增加石子printf("%d %d\n",a[i*2+1],a[i*2+1]+(sum^b[i])-b[i]);break;}}return 0;}

法二:

和尚是台阶数,每两人间隔的距离是石子数,转化为正常阶梯博弈的模型,取奇数石子堆进行Nim博弈。对于每个和尚,均枚举可以移动的距离,当偶数堆减少,相当于相邻的奇数堆增加,这也与法一直接取的奇数堆能增加或减少相同,只不过法一没有枚举可以增大或减少多少,而是通过异或操作判断能不能利用当前石子堆达到P态。对于阶梯博弈取奇数台阶进行Nim博弈的正确性,网上有相关证明。

#include <stdio.h>int main(){int a[105],b[105],i=0,j,k,count,sum=0;char c;while(1){scanf("%d%c",&a[i++],&c);if(c=='\n')break;}count=i;for(i=0;i<count-1;i++){//和尚是台阶,两人间隔的距离是石子数b[i]=a[i+1]-a[i]-1;}b[count-1]=0;for(i=0;i<count;i=i+2){sum^=b[i];}if(sum==0){printf("-1\n");}else{for(i=0;i<count;i++){for(j=1;j<=b[i];j++){b[i]-=j;//枚举所有能拿的方案if(i!=0){b[i-1]+=j;}sum=b[0];for(k=2;k<count;k=k+2){sum^=b[k];}if(sum==0){printf("%d %d\n",a[i],a[i]+j);break;}b[i]+=j;if(i!=0){b[i-1]-=j;}}}}return 0;}


5.格子刷油漆

X国的一段古城墙的顶端可以看成 2*N个格子组成的矩形(如图1所示),现需要把这些格子刷上保护漆。

你可以从任意一个格子刷起,刷完一格,可以移动到和它相邻的格子(对角相邻也算数),但不能移动到较远的格子(因为油漆未干不能踩!)

比如:a d b c e f 就是合格的刷漆顺序。

c e f d a b 是另一种合适的方案。

当已知 N 时,求总的方案数。当N较大时,结果会迅速增大,请把结果对 1000000007 (十亿零七) 取模。

输入数据为一个正整数(不大于1000)

输出数据为一个正整数。

例如:

用户输入:

2

程序应该输出:

24

再例如:

用户输入:

3

程序应该输出:

96

再例如:

用户输入:

22

程序应该输出:

359635897


此题若用深搜做,会超时。最佳方法是动态规划。

刷油漆可分两种情况:

一种是从端点(以左端点为例)出发,从端点出发又分①向右走不回头②先走同一列的,再随机走③先向右走,然后折回来,再向右走。

另一种是从中间出发,开始显然不能直接往下走,否则无法遍历所有点,应当是先遍历左边(右边)所有点,然后回到相对的点,然后遍历右边(左边)的点。注意先遍历的时候,必须是遍历全体格子后回到与之相对的格子,而后遍历则不受限制。设从第i列开始出发,出发点有两种选法,第一落脚点又有两种走法,后遍历的第一落脚点又有两种走法。

#include <stdio.h>long long a[1001],b[1001],sum;//a[i]存储的是i列格子刷油漆的所有方案,b[i]存储的是向一个方向走不回头的方案数#define NUM 1000000007int main() {int i,n;scanf("%d",&n);b[1]=1;//就是去储存一下2的n-1次方for (i=2; i<=n; i++)b[i]=(b[i-1]*2%NUM);  /*完全也可以用快速幂直接算long long Quick_Pow(int n){n--;long long a = 2,ans = 1;while(n > 0){if(n & 1){ans = (ans * a) % num;}n /= 2;a = (a * a) % num;}return ans % num;}*/a[1]=1;a[2]=6;for (i=3; i<=n; i++)a[i]=(2*a[i-1]+b[i]+4*a[i-2])%NUM;//b[i] 向右走不回头//2*a[i-1] 先走同一列的,再随机走//4*a[i-2] 先向右走,然后折回来,再向右走sum=4*a[n];  //上述的一个端点的情况。因此要乘以4for (i=2; i<n; i++) //中间的sum=((sum+8*b[n-i]*a[i-1]%NUM)%NUM+(8*a[n-i]*b[i-1])%NUM)%NUM;// 出发点在中间的时候,//显然不能直接往下走,//否则无法遍历所有点,//应当是先遍历左边(右边)所有点,//然后回到相对的点,然后遍历右边(左边)的点。//注意先遍历的时候,必须是遍历全体格子后回到与之相对的格子的走法,//否则无法遍历出发点正下方的点,而后遍历则不受限制。//因此设从第i列开始出发,出发点有两种选法,//第一落脚点又有两种走法,后遍历的第一落脚点又有两种走法,//走完总走法数为  2*(2*b[i-1]*2*a[n-i])+2*(2*b[n-i]*2*a[i-1])//即sum=((sum+8*b[n-i]*a[i-1]%NUM)%NUM+(8*a[n-i]*b[i-1])%NUM)%NUM;if(i == 1)sum = 2;printf("%lld\n",sum);return 0;}


1 0