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 + 5
3^3 = 27 = 7 + 9 + 11
4^3 = 64 = 1 + 3 + ... + 15
虽然他没有想出怎么证明,但他想通过计算机进行验证。
请你帮助小明写出 111 的立方之连续奇数和表示法的起始数字。如果有多个表示方案,选择起始数字小的方案。
法一:枚举首项和尾项,等差数列求和
#include <cstdio>
#include <iostream>
using namespace std;
int main(){
int a=111*111*111;//1367631
for(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 1000000007
int 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]; //上述的一个端点的情况。因此要乘以4
for (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;
}