思路:对于这道题,之前在写欧拉计划系列的时候曾经遇到过,当时用动态规划可以直接求出结果,这道题相当于是在基础上加了一个障碍点,在寻找路径的过程中需要跳过这些的障碍点,基本思路有了,接下来就认真分析一下怎么去实现这个算法。
分析:
1.状态转移方程
在二维坐标系中寻找从A点到B点的所有路径,而且只允许往右和往下,每次只能走一步,A点到自身的路径只有一条,这里假设B坐标为(i,j),那么上一步到达B的方式只有两种(i-1,j)和(i,j-1),从A到达B的所有路径和是在上一次走的路径基础上求和,那么就可以得出状态转移方程f(i,j)=f(i-1,j)+f(i,j-1)
2.跳过障碍点
玩过象棋的人应该知道,马走日(不知道也不要紧,题上给出了演示),那么我们根据输入的马的坐标可以得到马所控制的位置(马自身的位置也是被控制的,马一共控制九个位置),通过对马所控制位置做上标记,当用状态转移方程求路径和的时候跳过这些点就可以了。
3.需要注意
做完分析就该写代码了,这里有几个坑要避一下,数组越界,数组起始值为0,我们的起点应该定为1(防止状态转移方程数组越界),而又因为要对马能到达的位置做上标记,那么起点应该从2开始(防止对马能到达位置进行标记时越界),;开两个数组,对于java,定义一个数组的默认值是0,而我们对马到达的地方进行标记就不能用0了,而用其他数又会对结果造成影响(比如标记为-1,我们跳过障碍点了,但是障碍点附近的点求路径和的时候会把障碍点的值记录进去,对结果造成影响),所以这里一个数组作为标记数组,一个作为结果数组,最后输出结果数组就行。
package LOQ.递推与递归;
import java.util.Scanner;
/**
* 动态规划
* 起始位置只有一种路径,下一次走的位置的方法是由上一次所组成的,最终问题的解是由前面的解所构成
* 1.输入:B坐标和马的坐标
* 2.输出:从A到B的总路径数
* 算法
* //对马能到达的位置做上标记,包括马自身的位置
* dp[i][j]=-1
* res[2][2]=-1
* for i ← 2 to n do
* for j ← 2 to m do
* res[i][j]=res[i-1][j]+res[i][j-1];
*/
public class P1002过河卒 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
//防止出现越界,将坐标都向右下角平移一个单位
int n = sc.nextInt() + 2, m = sc.nextInt() + 2, b1 = sc.nextInt() + 2, b2 = sc.nextInt() + 2;
//定义二维坐标系 n+1为了包括n
int[][] dp = new int[n + 1][m + 1];
long[][] res = new long[n + 1][n + 1];
//将马能到达的位置做上标记,包括马的位置
dp[b1][b2] = -1;
dp[b1 + 2][b2 + 1] = -1;
dp[b1 + 1][b2 + 2] = -1;
dp[b1 - 1][b2 - 2] = -1;
dp[b1 - 2][b2 - 1] = -1;
dp[b1 - 1][b2 + 2] = -1;
dp[b1 - 2][b2 + 1] = -1;
dp[b1 + 2][b2 - 1] = -1;
dp[b1 + 1][b2 - 2] = -1;
dp[2][2] = -1;
//起点值为2
res[2][2] = 1;
for (int i = 2; i <= n; i++) {
for (int j = 2; j <= m; j++) {
//马可以到达 将结果集赋值
if (dp[i][j] == 0) {
res[i][j] = res[i - 1][j] + res[i][j - 1];
}
}
}
System.out.println(res[n][m]);
}
}