走方格
题目描述
核心思路
方法一:爆搜DFS,但是会TLE,时间复杂度是指数级别
#include<iostream>
using namespace std;
int n,m;
int ans; //记录答案
//从点(x,y)爆搜
void dfs(int x,int y)
{
//如果行数不为偶数或者列数不为偶数
if(x&1||y&1)
{
//当搜到了终点,则说明从起点到终点的这条路径是合法的方案
if(x==n&&y==m)
{
ans++; //合法方案 个数+1
return ; //回溯
}
//如果x还没走到n,那么可以往下走
if(x<n)
dfs(x+1,y);
//如果y还没有走到m,那么可以往右走
if(y<m)
dfs(x,y+1);
}
}
int main()
{
cin >>n>>m;
dfs(1,1); // 从起点 (1, 1) 开始搜索
cout <<ans<<endl;
return 0;
}
方法二:记忆化搜索,可以Ac
每个点只会被处理一次,一共有 n + m n+m n+m个点,所以总得时间复杂度是 O ( n + m ) O(n+m) O(n+m)
#include<iostream>
using namespace std;
const int N=35;
//f[n][m]表示从起点(1,1)走到终点(n,m)的路径条数,即方案数
int f[N][N]; //记忆化数组
int n,m;
//搜索点 (x, y),并返回从点 (x, y) 开始,能到点 (n, m) 的路径数量
int dfs(int x,int y)
{
//只要行数不为偶数或者列数不为偶数,则可以走过去 如果同时为偶数则不能走过去
if(x&1||y&1)
{
// 如果该点已经被搜索过,那么直接返回该点已经求出来的方案数即可
if(f[x][y])
return f[x][y];
//如果x还没走到n,那么可以往下走
if(x<n)
f[x][y]+=dfs(x+1,y);
//如果y还没有走到m,那么可以往右走
if(y<m)
f[x][y]+=dfs(x,y+1);
}
// 最后返回 f[x][y] 即可。如果 x, y 都是偶数,那么 f[x][y] 就没被处理过,必然为 0,可以不特判。
return f[x][y];
}
int main()
{
cin >>n>>m;
//给个错误数据 n=2,m=3.如果不在这里做特判,那么执行完dfs函数后得到的ans是0,方案数就为0
//但是从(1,1)->(1,2)->(1,3)->(2,3)是一条合法路径,因此正确的方案书就应该是1
f[n][m]=n&1||m&1; // 这里要特判下 n, m 是否都为偶数
int ans=dfs(1,1);
printf("%d\n",ans);
return 0;
}
方法三:动态规划 ,时间复杂度为 O ( n m ) O(nm) O(nm)
f [ i ] [ j ] f[i][j] f[i][j]表示从起点(1,1)走到点(i,j)的方案数。如果i和j都是偶数,那么特判为0,否则只能从上边或者左边转移过来。
状态转移方程: f [ i ] [ j ] = f [ i − 1 ] [ j ] + f [ i ] [ j − 1 ] f[i][j]=f[i-1][j]+f[i][j-1] f[i][j]=f[i−1][j]+f[i][j−1]
边界情况:第一行全部为1,即 f [ 1 ] [ j ] = 1 f[1][j]=1 f[1][j]=1;第一列全部为1,即 f [ i ] [ 1 ] = 1 f[i][1]=1 f[i][1]=1
#include<iostream>
using namespace std;
const int N=35;
//f[i][j]表示从起点(1,1)走到终点(n,m)的方案数
int f[N][N];
int n,m;
int main()
{
cin >>n>>m; //输入行数、列数
//最左边的第一列是边界,特殊处理方案数为1
//如果不特殊处理边界会出错,比如当i=2,j=1时,由f[i][j]=f[i-1][j]+f[i][j-1],可知f[2][1]=f[1][1]+f[2][0]
//f[2][0]使用全局变量为0是没有问题的,但是如果f[1][1]也使用全局变量的0就有问题了,因为从点(1,1)走到点(2,1)也是
//一种合法的方案
for(int i=1;i<=n;i++)
f[i][1]=1;
//最上边的第一行是边界,特殊处理方案数为1
//如果不特殊处理边界会出错,比如当i=1,j=2时,由f[i][j]=f[i-1][j]+f[i][j-1]可知,f[1][2]=f[0][2]+f[1][1]=0+0=0
//f[0][2]使用全局变量为0是没有问题的,但是如果f[1][1]也使用全局变量的0就有问题了,因为从点(1,1)走到点(1,2)也是
//一种合法的方案
for(int j=1;j<=m;j++)
f[1][j]=1;
//从第二行第二列开始
for(int i=2;i<=n;i++)
{
for(int j=2;j<=m;j++)
{
//只要行数不为偶数或者列数不为偶数,则可以走过去
//f[i-1][j]是从上方走下来
//f[i][j-1]是从左边走过来
if(i&1||j&1)
f[i][j]=f[i-1][j]+f[i][j-1];
}
}
cout <<f[n][m]<<endl;
return 0;
}