51nod 1572宝岛地图(动态规划预处理+模拟)

题目来源:  CodeForces
基准时间限制:1 秒 空间限制:131072 KB 分值: 20  难度:3级算法题
 收藏
 关注

勇敢的水手们到达了一个小岛,在这个小岛上,曾经有海盗在这里埋下了一些宝藏。然而,我们的船快抛锚了,与此同时,船长发现藏宝图的一角被老鼠咬掉了一块。

 

藏宝图可以用一个n×m大小的矩形表示。矩形中的每一小块表示小岛中的一小块陆地(方块的边长为1米)。有一些方块表示的是海,这些块人是不能通过的。除了海不能走,其它的小方块都是可以行走的。在可行走区域里有一些小方块表示一些已知的地点。

 

另外,在地图上有k条指令。每条指令的格式表示如下:

“向y方向走n米”。

 

这里的方向有四种:“北”,“南”,“东”,“西”。如果你正确的跟着这些指令行走,并且完整的执行完所有指令,你就可以找到宝藏所在的地点。

 

但是,很不幸,由于地图中好多地方都缺失了,船长也不知道从哪些地方开始走。但是船长依然清楚地记得一些已知的地点。另外,船长也知道所有可行走区域。

 

现在船长想知道从哪些已知地点出发,按照指令,可能找到宝藏所在地。


Input
单组测试数据
第一行包含两整数n和m(3≤n,m≤1000)。
接下来的n行每行有m个字符,表示整个地图。
“#”代表海。在地图矩形中,矩形的四周一圈一定是海。
“.”代表可行走区域,未知地点。大写字母“A”到“Z”表示可行走区域,已知地点。
所有大写字母不一定都被用到。每个字母在地图中最多出现一次。所有已知地点用不同的大写字母表示。

接下来一行有一个整数k(1≤k≤10^5),接下来有k行。
每行表示一条指令。
指令格式为“dir len”,“dir”表示朝哪个方向走,“len”表示走几步。
“dir”有四种取值“N”,“S”,“E”,“W”,对应题目中的“北”,“南”,“东”,“西”
在地图中,北是在顶部,南是在底部,西是在左边,东是在右边。“len”是一个整数,范围在[1,1000]。
Output
共一行,按字典序升序打印出所有可以完整执行地图中指令的已知区域的字母,如果没有满足要求的已知区域,则打印“no solution”(没有引号)。
Input示例
输入样例1
6 10
##########
#K#..#####
#.#..##.##
#..L.#...#
###D###A.#
##########
4
N 2
S 1
E 1
W 2
Output示例
输出样例1
AD
System Message  (题目提供者)

题解:显然是一个模拟题,一共10万次模拟,一共不超过26个模拟点,直接摸你就可以出结果。但是问题在于模拟的过程中,指令的执行必须要处理成O(1)的,不然要是一步一步走,肯定还是超时,第一次我暴力去预处理出来每一个点向四个方向运动的最大步数,试了一下有两组数据超时了。然后就去网上看看大佬的做法,原来问题就在于预处理的过程中计算了太多重复的结果,既然如此,用动态规划就好了,然后动态规划进行非常简单的预处理。
judge[i][j][0]表示(i,j)这个点向北“N”可以行走的最大步数,1,2,3表示“S”,“W”,“E”。然后就可以O(1)的时间之内执行指令了。
动态规划真的是博大精深。。。。
代码:
#include<bits/stdc++.h>
#include<iostream>
#include<cstring>
#include<math.h>
#include<stdlib.h>
#include<cstring>
#include<cstdio>
#include<utility>
#include<algorithm>
#include<map>
#include<stack>
#include<set>
#include<queue>
using namespace std;
typedef long long ll;
const int maxn = 1e3+5;
const int mod = 1e9+7;
const int Hash = 10000;
const int INF = 1<<30;
const ll llINF = 1e18+999;

int n,m,k,judge[maxn][maxn][4];//0,1,2,3分别表示N,S,W,E,
//例judge[i][j][0]表示从(i,j)出发,向北移动的最大步数不能超过judge[i][j][0]
char graph[maxn][maxn];
pair<char, int> ins[100005];
pair<int, int> pos[26];
void init( )
{
    memset(judge, -1, sizeof(judge));
    //这里分两步进行初始化,因为向左和向上是一样的,从左上角开始,向下和向右则是从右下角开始
    for(int i=0; i<n; i++)
        for(int j=0; j<m; j++)
        {
            if(graph[i][j] == '#')
               judge[i][j][0] = judge[i][j][2] = -1;
            else
            {
                judge[i][j][0] = judge[i-1][j][0] + 1;
                judge[i][j][2] = judge[i][j-1][2] + 1;
            }
        }
    //初始化向下和右的结果
    for(int i=n-1; i>-1; i--)
        for(int j=m-1; j>-1; j--)
        {
            if(graph[i][j] == '#')
               judge[i][j][1] = judge[i][j][3] = -1;
            else
            {
                judge[i][j][1] = judge[i+1][j][1] + 1;
                judge[i][j][3] = judge[i][j+1][3] + 1;
            }
        }
}

bool sim(int a, int b)
{
    for(int i=0; i<k; i++)
    {
        if(ins[i].first == 'N')
        {
            if(ins[i].second > judge[a][b][0])
                return false;
            a -= ins[i].second;
        }
        else if(ins[i].first == 'S')
        {
            if(ins[i].second > judge[a][b][1])
                return false;
            a += ins[i].second;
        }
        else if(ins[i].first == 'W')
        {
            if(ins[i].second>judge[a][b][2])
                return false;
            b -= ins[i].second;
        }
        else
        {
            if(ins[i].second > judge[a][b][3])
                return false;
            b += ins[i].second;
        }
    }
    return true;
}
int main( )
{
    //freopen("input.txt", "r", stdin);
    scanf("%d%d", &n,&m);
    int cnt = 0;
    char ans[30];
    for(int i=0; i<n; i++)
        for(int j=0; j<m; j++)
        {
            cin>>graph[i][j];
            if(graph[i][j]>='A' && graph[i][j]<='Z')
            {
                pos[cnt].first = i;
                pos[cnt++].second = j;
            }
        }
    init( );
    scanf("%d", &k);
    for(int i=0; i<k; i++)
        cin>>ins[i].first>>ins[i].second;
    int num = 0;
    for(int i=0; i<cnt; i++)
        if(sim(pos[i].first, pos[i].second))
            ans[num++] = graph[pos[i].first][pos[i].second];
    if(num == 0)
    {
        cout<<"no solution"<<endl;
        return 0;
    }
    sort(ans, ans+num);
    for(int i=0; i<num; i++)
        printf("%c", ans[i]);
    cout<<endl;
    return 0;
}

算法者,贵在积累,深入浅出。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值