记录一些题目,仅供个人学习用途
一、没睡醒的猫咪
题目:
可爱的很胖的猫咪小沁,与它的朋友们一起组成了一个猫咪合唱团。除开小沁之外,合唱团中总共有318只猫咪,编号依次为0~317。其中317只猫咪的音量都是100,剩下可能有一只猫咪因为没睡醒,所以音量只有90;现在我们要找出这只猫咪,或者确认所有猫咪都睡醒了。聪明的小沁每次可以指挥一组连续编号的猫咪发出声音,并记录它们的音量之和。但是猫咪们不是很有耐心,如果你指挥它们发出声音的次数超过18次,那么它们就都会四散跑掉,各自去玩啦。
小沁提供了这一函数int meow(int start, int end);
,它的输入是两个猫咪编号start
和end
,它会返回左闭右开区间[start, end)
中猫咪的音量之和;如果你输入的猫咪编号不合法,或者累计调用次数已达18次,那么它就会返回-1
。你需要完成函数int find_sleepy_cat(int start, int end);
,它的输入也是两个猫咪编号start
和end
,它需要返回左闭右开区间[start, end)
中没睡醒的那只猫咪的编号;如果这一区间当中所有猫咪都睡醒了,请返回-1
。
Input
没睡醒的猫咪的编号i
,i
的取值范围是[-10, 330]
。
如果输入的编号不合法,说明编号0~317的猫咪其实都睡醒了。
Output
输出没睡醒的猫咪的编号;如果所有猫咪都睡醒了,请输出-1
。
Hint
不需要关心函数int meow(int start, int end);
是如何实现的。
建议用递归哦:将猫咪们划分为两组,并分别观察每一组猫咪的音量之和是否偏小,对音量偏小的一组猫咪递归调用函数int find_sleepy_cat(int start, int end);
,直至区间[start, end)
中只剩下一只猫咪;如果两组猫咪的音量都正常,则说明区间内的全部猫咪都睡醒了。
分析
这道题要严格的分类讨论,找边界条件。题目多次出现的不合法返回-1,不合法指的就是end<=start(因为区间左闭右开,所以相等也是错的)。当递归到最后发现不是这个解的时候,也是返回-1。(matrix的题要做对真的是要乖乖听他的话呀)当递归到最后发现是这个解的时候,返回start(左闭右开)。是不是这个解以及在不在这个区间都是用meow来判断。
递归,要将终止条件先放在前面讨论。
#include "meow.h"
int find_sleepy_cat(int start, int end)
{
if (end <= start) //这里代表了18次以上与不合法输入
return -1;
else if (end - start == 1)
return meow(start, end)==90? start : -1;//遇到死路后我们也需要返回-1
else {
int middle = start + (end - start) / 2;
if (meow(start, middle)< (middle - start) * 100)
return find_sleepy_cat(start, middle);
else if (meow(middle, end) < (end - middle) * 100)
return find_sleepy_cat(middle, end);
else
return -1;
}
}
二、汉诺塔
他们说这只是递归的入门级别……、
Input
第一行是一个数字t,表示有t个测试用例
接下来的t行每一行是一个数字n,表示初始状态有n个盘子在木桩1上
Output
对于每一个用例,输出完成游戏的所有操作,每个移动操作占一行
Simple Input
1
3
Simple Output
move disk 1 from peg 1 to peg 3
move disk 2 from peg 1 to peg 2
move disk 1 from peg 3 to peg 2
move disk 3 from peg 1 to peg 3
move disk 1 from peg 2 to peg 1
move disk 2 from peg 2 to peg 3
move disk 1 from peg 1 to peg 3
分析
要使得第n组在C位置,则第n-1组要在B位置。要实现hanoi(n,a,b,c)就要有:
1) hanoi(n-1,a,c,b); //n-1从A搬到B
2) move(n-1,a,c); //n从A搬到C
3) hanoi(n-1,b,a,c); //n-1从B搬到C
终止条件:最后到n=1时,类比n的输入恰好只有1时,把唯一一个圆板从A搬到C。
#include<stdio.h>
void move(int n,int a,int b)
{
printf("move disk %d from peg %d to peg %d\n",n,a,b);
}
void Hanoi(int n,int a,int b ,int c)
{
if( n==1 )
{
move(1,a,c);
}
else
{
Hanoi(n-1,a,c,b);
move(n,a,c);
Hanoi(n-1,b,a,c);
}
}
int main(){
int t,n;
scanf("%d",&t);
while( t-- != 0 ){
scanf("%d",&n);
Hanoi(n,1,2,3);
}
return 0;
}
三、找零问题
小李是超市的收银员,每当顾客来结账时,他们给的钱往往都多于他们所购物品的实际价格,这时,小李就需要找零给他们。小李是一个很爱思考的人,他想知道在目前的纸币面额情况下(1角、5角、1元、5元、10元、20元、50元、100元),如果每种面额的纸币的数量都是无限的,他要给一个顾客找零N(0<N<100)元有多少种方式(如0.5元有两种方式:5个1角和1个5角)。小李很苦恼这个问题,聪明的你能帮助他吗?
Input
输入包括多组测试用例,通过EOF结束。
每组测试用例包括一行,为一个数N(1<N<100),N为最多包含一位小数的实数。
Output
对于每组测试用例,输出一行,为一个整数,表示找零的方法总数。
Sample Input
0.5
1
Sample Output
2
4
分析
这是两个参数的递归题
用return 1表示一种结果,累加起来得到答案。
#include<stdio.h>
int maz[8] = {1000,500,200,100,50,10,5,1};
//0 1 2 3 4 5 6 7
int zhaoqian(int n,int i)
{
if(i>7) return 0; //终止条件,优化
if(i==7) return 1;
if(n>=0&&n<5) return 1;
if(n<0) return 0;
while(n<maz[i]) i++;
//状态转移方程 ↓是在这里减的,上面都是判断而已
return zhaoqian(n,i+1) + zhaoqian(n-maz[i],i);//以700元为例,既要能反复减100(保留100对应
} //的i)又要能从1角开始反复减(保留700元)
int main()
{
double rmb=0;
while(scanf("%lf",&rmb) != EOF)
{
printf("%d\n",zhaoqian( rmb*10 ,0));
}
}
四、守望者的逃离(循环+分类讨论+最优解)
Description
恶魔猎手尤迪安野心勃勃,他背叛了暗夜精灵,率领深藏在海底的娜迦族企图叛变。守望者在与尤迪安的交锋中遭遇了围杀,被困在一个荒芜的大岛上。为了杀死守望者,尤迪安开始对这个荒岛施咒,这座岛很快就会沉下去。到那时,岛上的所有人都会遇难。
守望者的跑步速度为17m/s,以这样的速度是无法逃离荒岛的。庆幸的是守望者拥有闪烁法术,可在1s内移动60m,不过每次使用闪烁法术都会消耗魔法值10点。守望者的魔法值恢复的速度为4点/s,只有处在原地休息状态时才能恢复。
现在已知守望者的魔法初值M,他所在的初始位置与岛的出口之间的距离S,岛沉没的时间T。你的任务是写一个程序帮助守望者计算如何在最短的时间内逃离荒岛,若不能逃出,则输出守望者在剩下的时间内能走的最远距离。注意:守望者跑步、闪烁或休息活动均以秒(s)为单位,且每次活动的持续时间为整数秒。距离的单位为米(m)。
Input
输入包含多个测试数据(以EOF结束),每个测试数据仅一行,包括空格隔开的三个非负整数M, S, T。
0 <= M <= 1000, 1 <= S <= 10^6, 1 <= T <= 300000
Output
对每个测试数据输出两行:
第1行为字符串"Yes"或"No"(区分大小写),即守望者是否能逃离荒岛。
第2行包含一个整数。第一行为"Yes"(区分大小写)时表示守望者逃离荒岛的最短时间;第一行为"No"(区分大小写)时表示守望者能走的最远距离。
Sample Input
39 200 4
36 255 10
Sample Output
No
197
Yes
6
分析
变化量有三个:距离,时间,蓝。如果我们把时间当成自变量,让它一秒一秒的流逝,将会大大减少讨论的难度。
主题思路在代码里。
#include<stdio.h>
int main(){
int M,S,t; //蓝,距离、限时
int flash=0,sum=0,i;
while(scanf("%d%d%d",&M,&S,&t) != EOF) //用scanf判断EOF
{
flash=0;
sum=0; //sum在过程中表示跑步距离,在每次循环的最后会变成最大距离
for(i=1;i<=t;i++) //时间按秒流逝,以此做for循环,接下来只需要看路程
{ //思路改成:要么闪或补魔,要么跑步,他们都要花一秒
if(M>9) //如果蓝够,想都不用想,闪现就对了 但实际上闪现的过程是参与
//在等蓝的过程中的,因此要将其作为双线并进考量的一部分。
{
M -= 10;
flash += 60;
}
else //如果蓝不够了,双线并进,同时进行跑步与休息,取最大值
M+=4;
sum += 17; //sum保持最优解 ,双线并进就是最优解思想
if(sum<flash) sum = flash;
if(sum>=S) {
printf("Yes\n%d\n",i);
break;
}
} //现在离开了循环,要么因为sum够大了,要么因为超时了
if(i>t) printf("No\n%d\n",sum);
}
return 0;
}
五、爬楼梯70
分析:本质就是斐波那契,但是递归效率低,可用循环从下到上
class Solution {
public:
int climbStairs(int n) {
if(n == 1){return 1;}
if(n == 2){return 2;}
int a = 1, b = 2, temp;
for(int i = 3; i <= n; i++){
temp = a;
a = b;
b = temp + b;
}
return b;
}
};