题目描述
棋盘上 A 点有一个过河卒,需要走到目标 B 点。卒行走的规则:可以向下、或者向右。同时在棋盘上 C 点有一个对方的马,该马所在的点和所有跳跃一步可达的点称为对方马的控制点。因此称之为“马拦过河卒”。
棋盘用坐标表示,A 点(0,0)、B 点(n,m),同样马的位置坐标是需要给出的。
现在要求你计算出卒从 A 点能够到达 B 点的路径的条数,假设马的位置是固定不动的,并不是卒走一步马走一步。
输入格式
一行四个正整数,分别表示 B 点坐标和马的坐标。
输出格式
一个整数,表示所有的路径条数。
输入输出样例
输入 #1
6 6 3 3
输出 #1
6
说明/提示
对于 100%的数据,1≤n,m≤20,0≤a,b≤20。
算法知识——动态规划(DP)
一.动态规划算法基本思想:
如果各个子问题不是独立的(即重复的),且不同子问题的个数是有限的,那么如果我们可以保存已经解决的子问题的答案,而在需要时再找出已经求得的答案,这样就可以避免大量的重复计算,以此减少时间复杂度。
二.可以使用动态规划求解的问题需要满足以下基本特征:
1.最优子结构:即大问题的最优解包含子问题的最优解。
2.重叠子问题:即子问题的解会被多次用到。
3.无后效性:即求解大问题时不会改变子问题的最优解。
4.复杂问题线性化
三.实现思路:
1.定义数组,并明确其含义。
2.找出子问题和大问题之间的递推关系式(状态转移方程),并且用编程语言表达。
3.找出初始值,即无法使用第二步中关系式求解得出的值,在程序中需要手动赋值。
4.找出边界情况,在程序中需要特殊讨论。
题目思路
卒到达每一个点的路径条数由这个点上面以及左边的点的路径条数累加得到,所以考虑动态规划实现求解,直到规划至目标点。
代入到动态规划的实现思路中:
1.定义一个二维数组path[ ] [ ],记录卒到达每个坐标的路径条数。
注意题目中的数据范围,将path数据类型定为long long int。
long long int path[22][22];
2.找出递推关系式,即刚刚提到的卒到达每一个点的路径条数由这个点上面以及左边的点的路径条数累加得到。
path[n1][m1]=path[n1][m1-1]+path[n1-1][m1];
3.找出初始值,本题中为坐标原点。赋初值1。
path[1][1]=1;
4.找出边界情况。本题中坐标轴上的点不能从递推关系式得到,因为其左边或上边的值不在数组范围内。此时可以对这种情况进行分类讨论,也可以像AC代码中一样将程序中的坐标值定为题目中的坐标值加1。这样数组中多出来的path[0][ ]以及path[ ][0]元素就可以保证边界情况也可以用递推式得到。
此外,还要在程序中处理马控制点的问题。
AC代码
#include<stdio.h>
#include<string.h>
long long int path[22][22];
int hourse[8][2] = {{1,2},{2,1},{-1,2},{-2,1},{1,-2},{2,-1},{-1,-2},{-2,-1}};
int main(){
int n,m,a,b,n1,m1;
scanf("%d %d %d %d",&n,&m,&a,&b);
//memset函数初始化数组
memset(path,0,sizeof(path));
path[1][1] = 1;
//找控制点,标记为-1
path[a+1][b+1] = -1;
for(int i = 0; i < 8; i++){
if(a + hourse[i][0] >= 0 && b + hourse[i][1] >= 0)
path[a + hourse[i][0] + 1][b + hourse[i][1]+1] = -1;
}
//动态规划求解
for(n1 = 1; n1 <= n+1; n1++){
for(m1 = 1; m1 <= m+1; m1++){
if(path[n1][m1] == 0){
path[n1][m1] = path[n1][m1-1] + path[n1-1][m1];
}
else if(path[n1][m1] == -1){
path[n1][m1] = 0;
}
}
}
printf("%lld",path[n+1][m+1]);
return 0;
}
*实现细节
1.使用C中<string.h>头文件中的memset函数对path二维数组赋初值。
2.通过hourse数组在程序中循环找到控制点,减少复制粘贴工作量。