Maze Problem BNUOJ 1055

2 篇文章 0 订阅

Maze Problem BNUOJ 1055

Time Limit: 1000ms      
Memory Limit: 65535KB

走迷宫是很有趣的一种游戏,能够锻炼人的记忆力和思维.
现在,HK被困在一个迷宫里面了,请你帮助他计算一下有多少种不同的走法,能够让他走出迷宫.
这个迷宫很奇怪,HK只能够沿着向上或者向右的方向走,不能回头.

迷宫使用一个N*M的矩阵来描述,矩阵中用'.'代表空格可以通行,
用'*'代表障碍物,用'S'代表出发点,用'T'代表出口.例如下面的一个矩阵就描述了一个8*8的迷宫


.....T..
..*****.
......*.
*.***.*.
......*.
.****.*.
S..*....
........

Input

每个输入文件只包含一组输入数据.
每组数据第一行是两个正整数N和M(N,M<=100).
接着是一个N*M的矩阵.

Output

输出HK能够选用的不同方法数(由于结果可能很大,输出模1908的余数即可).
8 8
.....T..
..*****.
......*.
*.***.*.
......*.
.****.*.
S..*....
........

Sample Output
1

问题分析:
看到这个题我的第一反应是用dfs,从起点开始,一次检索两个方向上的可能性,
如果符合题意,则继续深入递归,直至到达终点,让count++并返回,
继续递归。于是我写了一个dfs的程序,本地测试了几组数据,都是正常产出。
我把代码交到oj,结果很意外:Time Limit Exceed. 我去?居然是超时。
但是仔细想想,就不难发现,这题就是个神坑。
为什么dfs会超时?因为在递归调用的时候,我重复计算了太多问题。
因为之前某种方法走到某一点后的状态,从这点到终点的方法数会在之后某种方法中途到底此点后再次调用dfs递归计算,这简直就是扯淡。
重复计算?想到了什么?
对,就是dp!

(注:
动态规划有两种基本结构:
一、带备忘的自顶向下的递归
就是把之前的算过的一些dfs值储存在一个数组里,把重复的递归调用转化为数组数据的直接访问。
二、自底向上的动态规划
就是把问题分成一系列子问题之后,找清楚各个子问题之间的相互依赖关系,即子问题的子问题,子子问题,然后从最小规模的子问题开始计算,自底向上,保证只有当某个问题的所有依赖子问题都计算完成后,才计算这个问题。
这两种算法时间界相近,第二种系数更小些。)

好了,那么改做法,dp
做法就不用多说了
下面附上代码
/**
 *name: Maze_2
 *p_ID: bnuoj 1055
 */
#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
using namespace std;

int N, M, x, y, xx, yy;
char str[101][101];
bool vis[101][101];
int dp[101][101];


void dpsearch(int p, int q)
{

    if(p>x || q<y || str[p][q]=='*') return;
    if(!vis[p][q])
    {
        vis[p][q] = true;
        dpsearch(p+1, q);
        dpsearch(p, q-1);
        if(p<N)
            dp[p][q] += dp[p+1][q]%1908;
        if(q>0)
            dp[p][q] += dp[p][q-1]%1908;
    }
}

int main()
{
    while(scanf("%d%d", &N, &M)!=EOF)
    {
        memset(str, '\0', sizeof(str));
        memset(vis, false, sizeof(vis));
        memset(dp, 0, sizeof(dp));
        int i=0;
        for(int i=0; i<N; i++)
            scanf("%s", str[i]);
        for(int i=0; i<N; i++)
        {
            for(int j=0; j<M; j++)
            {
                if(str[i][j]=='S')
                {
                    x = i;
                    y = j;
                }
                if(str[i][j]=='T')
                {
                    xx = i;
                    yy = j;
                }
            }
        }

        dp[x][y] = 1;
        dpsearch(xx, yy);
        printf("%d\n", dp[xx][yy]%1908);
    }

    return 0;
}
我把原来的dfs也附上,方便反省。。。
/**
 *name: Maze_2
 *p_ID: bnuoj 1055
 */
#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
using namespace std;

int N, M, x, y, num = 0;
int dir[][2] = {{-1,0}, {0, 1}};
char str[101][101];
bool vis[101][101];


bool easyGo(int x, int y)
{
    if(x>=0 && y>=0 && x<N && y<M && str[x][y]!='*' && !vis[x][y])
        return true;
    else return false;
}

void dfs(int x, int y)
{
    if(str[x][y]=='T')
    {
        num++;
        num %= 1908;
        return;
    }

    if(easyGo(x-1, y))
    {
        vis[x-1][y] = true;
        dfs(x-1, y);
        vis[x-1][y] = false;
    }
    if(easyGo(x, y+1))
    {
        vis[x][y+1] = true;
        dfs(x, y+1);
        vis[x][y+1] = false;
    }
    return;
}

int main()
{
    while(scanf("%d%d", &N, &M)!=EOF)
    {
        num = 0;
        memset(str, '\0', sizeof(str));
        memset(vis, false, sizeof(vis));
        int i=0;
        for(int i=0; i<N; i++)
            scanf("%s", str[i]);
        int flag = 1;
        for(int i=0; i<N; i++)
        {
            for(int j=0; j<M; j++)
            {
                if(str[i][j]=='S')
                {
                    x = i;
                    y = j;
                    vis[i][j] = true;
                    flag = 0;
                    break;
                }
            }
            if(flag==0)
                break;
        }
        dfs(x, y);
        printf("%d\n", num);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值