走方格

走方格

题目描述

在这里插入图片描述


核心思路

方法一:爆搜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[i1][j]+f[i][j1]

边界情况:第一行全部为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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卷心菜不卷Iris

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值