- 主题:请教一个用迭代函数解决青蛙跳台阶的问题(带程序)
自己在自学C语言,在做青蛙跳台阶的习题的时候遇到困难了。不知道有没有高手能给指
点一二:
题目是这样的:
//一共有r级台阶,青蛙一次可以跳1~m(m介于1至r之间)级台阶,青蛙只能向上跳,且
最后一跳刚好跳到最上面一级台阶(不能跳过).
//问一共有多少种跳法?并且将所有跳法罗列出来。
//比如3级台阶,青蛙一次最多跳3级的跳法一共有4种:111;12;21;3
目前我的程序能正确算出总的跳法,但是数组记录各种跳法遇到困难了。
这是程序运行的结果:
4 级台阶一次最多跳 2 级的跳法一共 5 种:
1 1 1 1 1
2 2 0 0 0
3 2 1 0 0
4 2 1 1 0
5 2 0 0 0
实际应该是:
1 1 1 1 1
2 1 1 2 0
3 1 2 1 0
4 2 1 1 0
5 2 2 0 0
原因是函数在递归的时候,归的那一下不是归到最前,而是归到倒数第2个跳步,所以造
成目前这种状况。但是我自己也不知道该如何解决这个问题,请教一下高手,用递归方
法解此题的话,是不是没有办法记录全部的跳法?如果能解决该如何改呢?
源程序如下:
#include <stdlib.h>
#include <stdio.h>
int BZ=0;//全局变量,统计一共有多少种跳法,当剩余级数每1次变零,这个数加1
int I=0;//记录跳法的数组的行坐标
int J=1;//记录跳法的数组的列坐标,第1列记录第几种方法,所以从第2列开始记录
void FJ(int RS,int MS) //FJ:Frog Jump;RS:Remain Steps;MS:青蛙可以跳的最大步数,小于RS
{ int i;//青蛙每次可能跳的步数
for(i=1;i<=__min(RS,MS);i++) //第一次跳无需考虑跳的最大级数大于剩余阶数的问题
{
if (RS-i>0) //未跳尽剩余台阶
{ FJ(RS-i,MS);
}
else//跳尽剩余台阶了
BZ=BZ+1;
}
}
void FR(int **p,int RS,int MS) //RS:Remain Steps;MS:青蛙可以跳的最大步数,小于RS
//这个FR函数只为记录每一种跳法的各跳级数
{ int i;//青蛙每次可能跳的步数
for(i=1;i<=__min(RS,MS);i++) //第一次跳无需考虑跳的最大级数大于剩余阶数的问题
{
if (RS-i>0)//未跳尽剩余台阶
{
p[I][J]=i;
J=J+1;
FR(p,RS-i,MS);
}
else//跳尽剩余台阶了
{
p[I][J]=i;//记录跳尽时最后一跳的级数
I=I+1;//每次跳尽,记录完后就换行记录一种新的跳法
J=1;//每次跳尽,记录完后从头开始记录
}
p[I-1][0]=I;//数组第一列记录是第几种跳法
}
}
int main()
{
int r;
int m;
printf("请输入台阶级数后按下回车键:\n");
scanf("%d",&r);
printf("请输入青蛙一次最多能跳几级台阶:\n");
scanf("%d",&m);
FJ(r,m);//调用之前定义的函数
printf("%d 级台阶一次最多跳 %d 级的跳法一共 %d 种:\n",r,m,BZ);
//下面要记录 每一种跳法的各跳级数了
int **p;//准备通过二级指针创建全0二维数组,用于存放每一种跳法
int j;
p=(int**)calloc(BZ+1,sizeof(int*));
for(j=0;j<BZ+1;j++)
{
p[j]=(int*)calloc(r+1,sizeof(int));
}
FR(p,r,m); //调用填写函数,将每一种跳法存进数组
int i;
for(i=0;i<BZ;i++)//打印出所有跳法组合
{
for(j=0;j<r+1;j++)
{
printf("%4d",p[i][j]);
if ((j!=0)&(j%(r)==0) )
printf("\n");
}
}
}
--
修改:sqsl FROM 219.142.154.*
FROM 111.193.227.*
这类不定方程问题的解法叫DFS(深度优先搜索),用数组记录搜索过的树上的路径
const int r = 5;
const int m = 3;
// 数组里多定义一个元素,减少代码里的边界判断。数组全初始化为0
int jumps[r + 1] = {}; // 记录每一次跳分别是几层台阶
int partialTotals[r + 1] = {}; // 记录当前已经跳的台阶总数,部分和
// 找到一个解,打印出来
void print(int step) {
for (int k = 1; k <= step; ++k)
printf("%d ", jumps[k]);
printf("\n");
}
// 跳一下。step表示当前是第几跳
void jump(int step) {
// 当前这一跳的台阶数,从1到m都尝试一下。
for (int k = 1; k <= m; ++k)
{
// 当前这一跳所跳的台阶数
jumps[step] = k;
// 到目前为止跳过的总台阶数,部分和
partialTotals[step] = partialTotals[step - 1] + k;
if (partialTotals[step] == r) {
// 找到一个解,并终止当前的尝试,剪枝。因为在当前这一跳,不可能再找到下一个解了
print(step);
break;
}
else if (partialTotals[step] > r) {
// 部分和大于整体和,终止当前的尝试,剪枝。因为在当前这一跳,不可能再找到下一个解了
break;
}
else {
// 部分和小于整体和,尝试下一跳
jump(step + 1);
}
}
}
int main()
{
// 从第一跳开始
jump(1);
return 0;
}
--
修改:z16166 FROM 221.218.160.*
FROM 221.218.160.*
感谢大牛分享的算法,涨知识了
不知道有没有大牛帮忙看看我的算法是否有补救的可能?也是为了涨知识,谢谢!
【 在 z16166 的大作中提到: 】
: 这类不定方程问题的解法叫DFS(深度优先搜索),用数组记录搜索过的树上的路径
: const int r = 5;
: const int m = 3;
: ...................
--
FROM 111.193.227.*
能抽时间把你的程序缩进一下吗?
【 在 sqsl 的大作中提到: 】
: 感谢大牛分享的算法,涨知识了
: 不知道有没有大牛帮忙看看我的算法是否有补救的可能?也是为了涨知识,谢谢!
:
--
FROM 167.220.233.*
#include <stdlib.h>
#include <stdio.h>
int BZ=0;//全局变量,统计一共有多少种跳法,当剩余级数每1次变零,这个数加1
int I=0;//记录跳法的数组的行坐标
int J=1;//记录跳法的数组的列坐标,第1列记录第几种方法,所以从第2列开始记录
void FJ(int RS,int MS) //FJ:Frog Jump;RS:Remain Steps;MS:青蛙可以跳的最大步数,小于RS
{ int i;//青蛙每次可能跳的步数
for(i=1;i<=__min(RS,MS);i++) //第一次跳无需考虑跳的最大级数大于剩余阶数的问题
{
if (RS-i>0) //未跳尽剩余台阶
{ FJ(RS-i,MS);
}
else//跳尽剩余台阶了
BZ=BZ+1;
}
}
void FR(int **p,int RS,int MS) //RS:Remain Steps;MS:青蛙可以跳的最大步数,小于RS
//这个FR函数只为记录每一种跳法的各跳级数
{ int i;//青蛙每次可能跳的步数
for(i=1;i<=__min(RS,MS);i++) //第一次跳无需考虑跳的最大级数大于剩余阶数的问题
{
if (RS-i>0)//未跳尽剩余台阶
{
p[I][J]=i;
J=J+1;
FR(p,RS-i,MS);
}
else//跳尽剩余台阶了
{
p[I][J]=i;//记录跳尽时最后一跳的级数
I=I+1;//每次跳尽,记录完后就换行记录一种新的跳法
J=1;//每次跳尽,记录完后从头开始记录
}
p[I-1][0]=I;//数组第一列记录是第几种跳法
}
}
int main()
{
int r;
int m;
printf("请输入台阶级数后按下回车键:\n");
scanf("%d",&r);
printf("请输入青蛙一次最多能跳几级台阶:\n");
scanf("%d",&m);
FJ(r,m);//调用之前定义的函数
printf("%d 级台阶一次最多跳 %d 级的跳法一共 %d 种:\n",r,m,BZ);
//下面要记录 每一种跳法的各跳级数了
int **p;//准备通过二级指针创建全0二维数组,用于存放每一种跳法
int j;
p=(int**)calloc(BZ+1,sizeof(int*));
for(j=0;j<BZ+1;j++)
{
p[j]=(int*)calloc(r+1,sizeof(int));
}
FR(p,r,m); //调用填写函数,将每一种跳法存进数组
int i;
for(i=0;i<BZ;i++)//打印出所有跳法组合
{
for(j=0;j<r+1;j++)
{
printf("%4d",p[i][j]);
if ((j!=0)&(j%(r)==0) )
printf("\n");
}
}
}
【 在 mango7788 的大作中提到: 】
: 能抽时间把你的程序缩进一下吗?
--
修改:sqsl FROM 219.142.154.*
FROM 219.142.154.*
刚才我回复的不见了。
你的第一个函数应该是对的,第二个太长了没有看。
我在你第一个函数里加了两行记录路径的。路径可以当参数传进来,我懒,就搞了全局的。
你为啥要写这个啊?
int steps[100];
void print(int k)
{
for (int i = 0; i < k; ++i)
{
printf("%d ", steps[i]);
}
printf("\n");
}
void FJ(int RS, int MS, int k)
{
int i;
for (i = 1; i <= __min(RS, MS); i++)
{
steps[k] = i;
if (RS - i > 0)
{
FJ(RS - i, MS, k + 1);
}
else
{
print(k+1);
}
}
}
int main()
{
FJ(4, 2, 0);
}
--
FROM 167.220.233.*
我第2个函数几乎就是第1个翻版,只是调用一个数组存每一次跳了多少步。但是因为函
数迭代的结构,不是所有跳法都能记录齐全
我就是在学编程,遇到困难想解决
【 在 mango7788 的大作中提到: 】
: 刚才我回复的不见了。
: 你的第一个函数应该是对的,第二个太长了没有看。
: 我在你第一个函数里加了两行记录路径的。路径可以当参数传进来,我懒,就搞了全局的。
: ...................
--
FROM 111.193.227.*
我没有看你打印的那部分code,但我可以直观的解释一下:
如果我们站在青蛙的角度,往回看我们上来的路径,就是要打印的信息。
当青蛙尝试另外一条路的时候,是把自己拉回了一个曾经经过的位置,重新跳。
所以这个信息用一个栈来记录是比较直观的,如果把之前的路径传进来,就用调用栈实现了这个栈。
你用二维数组来存,因为不直观,所以就不容易。
又看了一下你第二个函数,因为要拷贝之前的路径,在 else 里至少要有个循环才行。
【 在 sqsl 的大作中提到: 】
: 我第2个函数几乎就是第1个翻版,只是调用一个数组存每一次跳了多少步。但是因为函
: 数迭代的结构,不是所有跳法都能记录齐全
: 我就是在学编程,遇到困难想解决
: ...................
--
修改:mango7788 FROM 167.220.233.*
FROM 167.220.233.*
是的,您分析的很对,我按这个思路去改一改看,成功了回来回复,不过今天有点忙,
可能搞不出来
【 在 mango7788 的大作中提到: 】
: 我没有看你打印的那部分code,但我可以直观的解释一下:
: 如果我们站在青蛙的角度,往回看我们上来的路径,就是要打印的信息。
: 当青蛙尝试另外一条路的时候,是把自己拉回了一个曾经经过的位置,重新跳。
: ...................
--
FROM 111.193.227.*
有两处错误
1、(RS - i)和0比较是三种情况,不是两种。
你的代码会导致(RS - i) < 0这种无解的情况下,也会执行BZ = BZ + 1,这样解的总数BZ是错的。(但这个题里正好没有错)
2、得到一个解后将J复位到1,是错的。
如果当前递归层数是n,那么当前一定是第n跳。
但是如果此时n并不是1,那么强行将J置为1,就会把第n跳(或者第x跳)的台阶数记录到第1跳里,是错的。
可以看一下我提供的代码,传给jump(int step)的参数step就是表示当前是第几跳,同时也对应着递归的层数。
这个step就是你的那个J变量,也就是7楼说的栈顶索引,是不能随便复位的。换句话说,J最好通过参数传递(这样天生就是栈顶索引),而不是用全局变量。
【 在 sqsl 的大作中提到: 】
: 我第2个函数几乎就是第1个翻版,只是调用一个数组存每一次跳了多少步。但是因为函
: 数迭代的结构,不是所有跳法都能记录齐全
: 我就是在学编程,遇到困难想解决
: ...................
--
修改:z16166 FROM 221.218.160.*
FROM 221.218.160.*