学习数据结构的第二十一天
动态规划
怎么说呢,不学不知道,一学吓一跳,真的,到时候一看出来,就只知道两个字,妙啊!
先看题目
[蓝桥杯 2020 省 AB1] 走方格
题目描述
在平面上有一些二维的点阵。
这些点的编号就像二维数组的编号一样,从上到下依次为第 1 1 1 至第 n n n 行,从左到右依次为第 1 1 1 至第 m m m 列,每一个点可以用行号和列号来表示。
现在有个人站在第 1 1 1 行第 1 1 1 列,要走到第 n n n 行第 m m m 列。只能向右或者向下走。
注意,如果行号和列数都是偶数,不能走入这一格中。
问有多少种方案。
输入格式
输入一行包含两个整数 n n n, m m m。
输出格式
输出一个整数,表示答案。
样例 #1
样例输入 #1
3 4
样例输出 #1
2
提示
1 ≤ n , m ≤ 30 1\le n,m\le30 1≤n,m≤30。
蓝桥杯 2020 第一轮省赛 A 组 G 题(B 组 H 题)。
一看到的时候,我直接就是递归深搜。先看我递归的代码。
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
int m,n,sum;
int book[31][31];//标记
void dfs(int s,int l)
{
if(s==n&&l==m)
{
sum++;
return;
}
int next[2][2]={{0,1},{1,0}};//方向数组
int ty,tx;
for(int j=0;j<=1;j++)
{
ty=s+next[j][0];
tx=l+next[j][1];//下一个位置的坐标
if(tx<1||tx>m||ty<1||ty>n)
{
continue;
}
if(book[ty][tx]==0)//如果没走或者没障碍物,就走
{
book[ty][tx]=1;
dfs(ty,tx);
book[ty][tx]=0;//收回
}
}
return;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
if(i % 2 == 0 && j % 2 == 0)
{
book[i][j] = 1;//标记障碍物
}
dfs(1,1);
printf("%d",sum);
return 0;
}
但是啊,你会发现一个问题,我交上去的时候超时了,这是什么原因呢?原因就在于做了多余的判断和部署。怎么理解呢?如果(2,2)这个点不能走,那么我许多次递归都要判断一次,这样就浪费时间,并且,深搜是不断实验,那么我会有多少次重复实验这个不能走的点呢?这样就相当的浪费时间。
那么接下来我觉得秒的方法我就来解释一下。如果我们现在在(3,2)这个点怎么走?是不是只能走到(3,3),如果我们在(2,3)这个点呢?这就是问题所在了,我是不是只能走到(3,3),你发现么有?当两个数之间有一个为偶数时,我们是不是只能选择把偶数加一才能走下去。有人问为什么。大哥,双偶数不能走啊。对不对?那么为双奇数时呢?是不是都可以?那么,是不是出现了一种更好的递归方式。用奇偶来判断递归。好处显而易见,不用再过多的判断是不是能递归的点,直接避开了那个方法,这简直就是太酷啦。
那么上代码。
#include<bits/stdc++.h>
using namespace std;
int sum=0;
int n,m;
void dp(int x,int y)
{
if(x==1||y==1)
{
sum++;
return;
}
else if(x*y%2==0)
{
if(x%2==0)//x为偶数的情况
dp(x-1,y);
else//y
dp(x,y-1);
}
else
{
dp(x-1,y);//两种情况
dp(x,y-1);
}
return;
}
int main()
{
cin>>n>>m;
dp(n,m);//采用的倒搜啊,因为不易越界
cout<<sum;//输出
return 0;
}