洛谷P1002 过河卒的dfs和dp题解

过河卒算是很经典的一道题了吧,NOIP 2002年的题目,我在学习递推的时候遇上的,明明是一道dp题,我看数据不是非常大(1≤n,m≤20,0≤马的坐标≤20)然后我就试了一下dfs,想练练手防止搜索忘了。
上代码:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int ans = 0;
bool vis[25][25];
int a, b, n, m;
int x[] = {0, 1, 0};
int y[] = {0, 0, 1};
int d[9][2] = {{0, 0}, {1, 2}, {1, -2}, {-1, 2}, {-1, -2}, {2, 1}, {2, -1}, {-2, 1}, {-2, -1}};//马的活动范围

bool judge(int temx, int temy)//返回true为马可以抓住卒,false则不能
{
    for(int i = 0; i < 9; i++)
    {
        if(a + d[i][0] == temx && b + d[i][1] == temy) return true;        
    }
    return false;
}

void dfs(int temx, int temy)
{
    if(temx > n || temx < 0 || temy > m || temy < 0 || judge(temx, temy)) return ;
    if(temx == n && temy == m)
    {
        ans ++;
        return ;
    }
    for(int i = 1; i <= 2; i++)
    {
        if(vis[temx + x[i]][temy + y[i]])
        {
            vis[temx + x[i]][temy + y[i]] = false;
            dfs(temx + x[i], temy + y[i]);
            vis[temx + x[i]][temy + y[i]] = true;
        }
    }
    return ;
}


int main()
{
    memset(vis, true, sizeof(vis));
    scanf("%d%d%d%d", &n, &m, &a, &b);
    dfs(0, 0);
    printf("%d\n", ans);
    return 0;
}

提交过后:
在这里插入图片描述
完美T掉三个点。
我就想为啥,后来发现递归40次到没多大事,但是在20*20的时候有C(20,40)种,至少为10的20次方种可能性,那不绝对T掉了吗T^T

后来重新老老实实地写了个dp:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int ans = 0;
typedef long long ll;
ll vis[25][25];
int a, b, n, m;
int x[] = {0, 1, 0};
int y[] = {0, 0, 1};
int d[9][2] = {{0, 0}, {1, 2}, {1, -2}, {-1, 2}, {-1, -2}, {2, 1}, {2, -1}, {-2, 1}, {-2, -1}};//马的活动范围

bool judge(int temx, int temy)//返回true为马可以抓住卒,false则不能
{
    for(int i = 0; i < 9; i++)
    {
        if(a + d[i][0] >= 0 && a + d[i][0] < n && b + d[i][1] >=0 && b + d[i][1] <=m)
        {
            if(a + d[i][0] == temx && b + d[i][1] == temy) return true;        
        }
    }
    return false;
}

int main()
{
    memset(vis, 0, sizeof(vis));
    scanf("%d%d%d%d", &n, &m, &a, &b);
    if(judge(0,0)) vis[0][0] = 0;
    else vis[0][0] = 1;
    for(int i = 0; i <= n; i++)
    {
        for(int j = 0; j<= m; j++)
        {
            if(!judge(i, j))
            {
                if(i!=0) vis[i][j] += vis[i-1][j];
                if(j!=0) vis[i][j] += vis[i][j-1];
            }
        }
    }
    printf("%lld\n", vis[n][m]);
    return 0;
}

提交了一下:
在这里插入图片描述
完美~
总结一下:
做题前先估算一下数据规模再写,别好不容易写完发现T了又要重新优化;
遇到状态转移的情况,后面状况受前面的状况影响时,考虑用dp;
dp的话累和数据可能很大,注意考虑数据结构和优化。

  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
过河卒是一个典型的动态规划问题。首先,我们将整个棋盘看作一个二维数组,数组的每个元素表示到达该位置的路径数目。然后,我们根据题目给出的条件,逐步更新数组中的元素,直到计算出到达目标位置的路径数目。 具体的解题思路如下: 1. 首先,我们可以将马的位置设置为0,表示无法经过该位置。 2. 然后,我们根据马的位置,更新数组中的元素。对于二维数组中的每个位置,我们根据左边和上边的位置来计算到达当前位置的路径数目。具体地,如果左边和上边的位置都可以经过,那么到达当前位置的路径数目就等于左边和上边位置的路径数目之和。如果左边或上边的位置无法经过,那么到达当前位置的路径数目就等于左边或上边位置的路径数目。 3. 最后,我们输出目标位置的路径数目。 下面是洛谷p1002过河卒题解的C++代码: ```cpp #include <bits/stdc++.h> using namespace std; int main() { long long a[21][21]; int x1, y1, x2, y2; cin >> x1 >> y1 >> x2 >> y2; // 初始化数组,马的位置设置为0 for(int i=0; i<=20; i++) { for(int k=0; k<=20; k++) { a[i][k] = 1; } } a[x2][y2] = 0; // 根据马的位置更新数组中的元素 if(x2 >= 2 && y2 >= 1) a[x2-2][y2-1] = 0; if(x2 >= 1 && y2 >= 2) a[x2-1][y2-2] = 0; if(x2 <= 18 && y2 >= 1) a[x2+2][y2-1] = 0; if(x2 <= 19 && y2 >= 2) a[x2+1][y2-2] = 0; if(x2 >= 2) a[x2-2][y2+1] = 0; if(x2 >= 1) a[x2-1][y2+2] = 0; if(y2 >= 1) a[x2+2][y2-1] = 0; if(y2 >= 2) a[x2+1][y2-2] = 0; // 动态规划计算路径数目 for(int i=1; i<=20; i++) { for(int k=1; k<=20; k++) { if(a[i][k] != 0) { a[i][k] = a[i-1][k] + a[i][k-1]; } } } // 输出目标位置的路径数目 cout << a[x1][y1] << endl; return 0; } ```
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值