题目描述:
核心思想:递归
A点到B点 采用每次一步一步的走
A——>B的路径条数= C——>B + D——>B
故可以看出 A到B的路径条数 等于 A左边的一格 及 下边的一格 到B的路径之和
故递归思想就出来了
C——>B = D到B + E到B
D——>B=E到B + F到B
…………
函数代码:
//旧版 P1002 [NOIP2002 普及组] 过河卒
#include<stdio.h>
#include<stdlib.h>
int x = 0, y = 0;//马的位置
int main()
{
int n = 0, m = 0;//终点位置
int i = 0, j = 0;//此时卒的位置
int long long lujin(int n, int m, int i, int j);//申明
scanf("%d %d %d %d", &n, &m, &x, &y);
if (((x == 0) & (y == 0)) || ((x == n) && (y == m)) || (((abs(n - x) + abs(m - y)) == 3) && ((n != x) && (abs(m - y) != 3)) && ((m != y) && (abs(n - x) != 3))))
printf("0");
else
{
int long long zu = lujin(n, m, i, j);
printf("%lld", zu);
}
return 0;
}
int long long lujin(int n, int m, int i, int j)
{
if ((i + 1 == n) && (j == m))
return 1;
if ((i == n) && (j + 1 == m))
return 1;
if ((i + 1 <= n) && (j <= m) && (((abs(i + 1 - x) + abs(j - y)) != 3) || ((i + 1 == x) && (abs(j - y) == 3)) || ((j == y) && (abs(i + 1 - x) == 3))) && (((i + 1) != x) || (j != y)))// 判断x方向的这一步有否有效 马控制的全范围,(n,m)表格也没事 反正卒走不到那些位置
{
if ((i <= n) && (j + 1 <= m) && (((abs(i - x) + abs(j + 1 - y)) != 3) || ((i == x) && (abs(j + 1 - y) == 3)) || ((j + 1 == y) && (abs(i - x) == 3))) && ((i != x) || ((j + 1) != y)))//判断y方向这一步是否有效
return (lujin(n, m, i + 1, j) + lujin(n, m, i, j + 1)); //都有的话 有两种路劲
else
return(lujin(n, m, i + 1, j)); //就x方向这一步有效
}
else if ((i <= n) && (j + 1 <= m) && (((abs(i - x) + abs(j + 1 - y)) != 3) || ((i == x) && (abs(j + 1 - y) == 3)) || ((j + 1 == y) && (abs(i - x) == 3))) && ((i != x) || ((j + 1) != y)))//判断y方向这一步是否有效
return(lujin(n, m, i, j + 1));
else
return 0;//x y 方向都无效 }
}
代码解释:因为马的位置可为
所以马可能在 起点(0,0)位置 也可能在终点位置 所以这种情况就直接 输出0 表示没有路劲可达到。
这部分是 马可以跳 到的地方
就这张图来看 它能到达的位置是这个菱形 但是要抛去 红色四个点 关系均为
其中 函数abs()是绝对值函数
这部分及排除了 红色的点部分。
函数部分:
因为返回值可能比较大 故采用 long long类型 传递四个参数
递归参数为 6个参数也可以 带上X Y马的位置
运行截图:
该算法有个缺陷: n m取比较大时 跑不出来 例如n=m=20
原因分析:是递归次数的指数型增长
这里的二次的指数型增长 以及重复递归的结果
(0,0)递归到(0,1)和(1,0)
(0,1)递归到(0,2)和(1,1)
(1,0)递归到(1,2)和(1,1)
依次类推会 2的指数型增长 许多重复性递归
所以在答题系统里跑不过去 只能通过三个测试
优化思路:就是避免它的重复递归
比如这里(1,1)到终点的路径次数是固定不变的,不用每次都计算,而把第一次计算的值存下来,后面遇到了直接拿来使用!!!
原码如下:
//新版P1002 [NOIP2002 普及组] 过河卒
#include<stdio.h>
#include<stdlib.h>
int x = 0, y = 0;//马的位置
int long long arr[21][21] = { 0 };
int main()
{
int n=0, m = 0;//终点位置
int i = 0, j = 0;//此时卒的位置
int long long lujin(int n, int m,int i, int j);//申明
scanf("%d %d %d %d", &n,&m,&x,&y);
if (((x == 0) & (y == 0)) || ((x == n) && (y == m)) || (((abs(n - x) + abs(m - y)) == 3) && ((n != x) && (abs(m - y) != 3)) && ((m != y) && (abs(n - x) != 3))))
printf("0");
else
{
int long long zu=lujin(n, m, i, j);
printf("%lld", zu);
}
return 0;
}
int long long lujin(int n, int m, int i, int j)
{
if ((i + 1 == n) && (j == m))
return 1;
if ((i == n) && (j + 1 == m))
return 1;
if ((i + 1 <= n) && (j <= m) && (((abs(i + 1 - x) + abs(j - y)) != 3) || ((i + 1 == x) && (abs(j - y) == 3)) || ((j == y) && (abs(i + 1 - x) == 3))) && (((i + 1) != x) || (j != y)))// 判断x方向的这一步有否有效 马控制的全范围,(n,m)表格也没事 反正卒走不到那些位置
{
if ((i <= n) && (j + 1 <= m) && (((abs(i - x) + abs(j + 1 - y)) != 3) || ((i == x) && (abs(j + 1 - y) == 3)) || ((j + 1 == y) && (abs(i - x) == 3))) && ((i != x) || ((j + 1) != y)))//判断y方向这一步是否有效
{
if (arr[i + 1][j] == 0)
{
arr[i + 1][j] = lujin(n, m, i + 1, j);
if (arr[i][j + 1] == 0)
{
arr[i][j + 1] = lujin(n, m, i, j + 1);
return(arr[i][j + 1] + arr[i + 1][j]);
}
else
return(arr[i][j + 1] + arr[i + 1][j]);
}
else
{
if (arr[i][j + 1] == 0)
{
arr[i][j + 1] = lujin(n, m, i, j + 1);
return(arr[i][j + 1] + arr[i + 1][j]);
}
else
return(arr[i][j + 1] + arr[i + 1][j]);
}
//return (lujin(n, m, i + 1, j) + lujin(n, m, i, j + 1)); //都有的话 有两种路劲
}
else
{
if (arr[i + 1][j] == 0)
{
arr[i + 1][j] = lujin(n, m, i + 1, j);
return arr[i + 1][j];
}
else
return arr[i + 1][j];
//return(lujin(n, m, i + 1, j)); //就x方向这一步有效
}
}
else if ((i <= n) && (j + 1 <= m) && (((abs(i - x) + abs(j + 1 - y)) != 3) || ((i == x) && (abs(j + 1 - y) == 3)) || ((j + 1 == y) && (abs(i - x) == 3))) && ((i != x) || ((j + 1) != y)))//判断y方向这一步是否有效
{
if (arr[i][j+1] == 0)
{
arr[i][j+1] = lujin(n, m, i, j+1);
return arr[i][j+1];
}
else
return arr[i][j+1];
//return(lujin(n, m, i, j + 1));
}
else
return 0;//x y 方向都无效
}
加了一个二维数组 用来记录每一个坐标 是否递归过 使用全局变量是因为它要在函数中使用,减少递归参数 故放在全局变量
用该语句替换之前的语句。
例如此时 i=a j=b
递归之前 先判断arr[a][b]处的值 如果为初试值0 则为该位置未递归 所以采用递归并赋值及该位置就存下了 (a,b)到终点的路劲次数 后面用到了直接 return arr[a][b]即可 大大减小递归次数
同理后面递归的地方 只要是递归 就先判断递归坐标这个地方 是否 递归过
运行演示:秒出结果 运行速度大大提高
测试也均通过