小羽毛想吃辣条
题目背景
小羽毛和她的邻居约好了一起减肥,可是她想吃辣条。实在是忍不住家门口那个超市的诱惑,小羽毛悄咪咪跑出去买了一包辣条。出了超市门的小羽毛一眼就看见了邻居,不行,不能让邻居知道自己跑出来买辣条!
小羽毛的家和超市仅仅隔着一个矩形小广场,所以她还是有希望迅速跑回家而不被邻居发现的。
而邻居现在正在跟人聊天,所以只要不走得离邻居太近,邻居就不会发现她。她现在想知道自己有多少条路可以回家而不被邻居发现。
题目描述
以小羽毛的位置为原点,给出邻居和小羽毛家的位置坐标。由于小羽毛想快一点儿回家,她只向下或者向右走。
邻居和朋友聊天聊得实在太入迷了。只要不擦着邻居身边走过,邻居就不会发现她。
样例输入如下图所示,以小羽毛的位置为基准(0,0),她的家在黄色点(4,4)。棕色点是邻居的位置,绿色点即是邻居的身边,也不可以走
输入格式
第一行两个数字 n , m n,m n,m,表示小羽毛家的位置 ( n , m ) (n, m) (n,m)
第二行两个数字 x , y x ,y x,y,表示邻居所在位置 ( x , y ) (x, y) (x,y)
输出格式
一行,为小羽毛可以顺利走回家不被邻居发现的路径数量
样例一
输入
4 4
2 2
输出
4
提示说明
0 < n , m , x , y ≤ 25 0<n, m, x, y≤25 0<n,m,x,y≤25,小羽毛的位置在 ( 0 , 0 ) (0,0) (0,0)
题解
动态规划,因为小羽毛只向右或者向下走,所以每一个位置只可能是从其上,或者其左边来。即:每一个位置的可行路径数为其上一个位置和左边一个位置的可行路径数之和。
动态规划三步走:
- 原问题:求从
(0,0)
走到(n,m)
的可行路径数量 - 子问题:从
(0,0)
走到图中任一点(i,j)
的可行路径数量 - 用
dp[i][j]
数组来代表走到(i, j)
的可行路径数量。转移方程:dp[i][j] = dp[i-1][j] + dp[i][j-1]
本题注意几个细节:
- 数据范围到25,我们可以忽略掉不能走的那几个位置,根据排列组合来估计结果数据范围:$C_{50}^{25} = 126410606437752 $ 已经超过了
int
的范围(2147483647)。但是仍在long long
之内。所以我们的dp
数组必须开到long long
- 输入邻居的位置以后,可以采取事先标记的方法。即标记图中所有不能走的点。然后再对整个
dp
数组进行计算。但是这种方法需要特判的情况比较多。还要注意用于标记的值以及赋值语句的顺序,比较麻烦。不推荐 - 由于地图矩阵是从
(0,0)
开始,到(n,m)
结束。输入n
和m
以后才开动态规划数组的话记得+1否则会越界
具体实现 推荐直接在dp
数组的计算过程中特判第一行、第一列和不能走的情况。
标程
#include<bits/stdc++.h>
using namespace std;
int main(){
int n,m,x,y,i,j;
cin>>n>>m>>x>>y;
long long dp[n+1][m+1];
//注意加一防止越界,longlong防爆
dp[1][0]=dp[0][1]=dp[0][0]=1;
for(i=0;i<=n;i++){
for(j=0;j<=m;j++){
if(i==x&&j==y-1||i==x&&j==y+1||
i==x+1&&j==y||i==x-1&&j==y||i==x&&j==y){
dp[i][j]=0; //不可走点,通行条数为0
}
else if(0==i&&j) //第一行
dp[i][j]=dp[i][j-1];
else if(0==j&&i) //第一列
dp[i][j]=dp[i-1][j];
else if(i&&j) //转移方程
dp[i][j]=dp[i-1][j]+dp[i][j-1];
}
}
cout<<dp[n][m]; //输出走到(n,m)的可行路径数量
return 0;
}