洛谷 P6226 [BalticOI 2019 Day1] 潜艇 题解

前言

(打个广告)我的洛谷博客

S 组模拟赛的 T1,在考场没做对,交发题解加深下印象。

题目分析

第一眼见到这道题目时,想到的是 Sub1 的暴力解,枚举整个矩阵中的海洋,然后直接按照信号模拟潜水艇的路径上是否有陆地。其次,我们可以想到,对于信号中的 '?',可以将其拓展成四个方向,再进行枚举。

进一步的优化,我们可以发现对于同一个终点,可能会有超过 1 个的起点可以到达,我们如果分别枚举每个点,可能会发生重复的情况。所以考虑到是否可以对整个矩阵直接进行模拟。

设计状态 f k , i , j f_{k, i, j} fk,i,j 表示在处理信号到第 k k k 位时,潜水艇有没有可能出现在第 i i i j j j 列。在处理信号时根据信号中的方向转移,时间复杂度为 O ( R C M ) O(RCM) O(RCM),可以获得 66 分。

考虑再次优化,首先将三位压为两位, f k , ( i − 1 ) × n + j f_{k, (i - 1) \times n + j} fk,(i1)×n+j 表示在处理信号到第 k k k 位时,潜水艇有没有可能出现在第 i i i j j j 列。再考虑状态压缩,用 bitset(点击直达 oi - wiki) 优化,将转移直接写成位移,不能在陆地上用按位与运算操作。可获得时间复杂度为 O ( R C M w ) O(\frac{RCM}{w}) O(wRCM) 的可行算法。

代码

#include<bits/stdc++.h>
using namespace std;
const int NR = 5 * 1e2;
const int MR = 5 * 1e3;
char a[NR + 10][NR + 10];
char s[MR + 10];
bitset<NR * NR + 10> bs;//陆地
bitset<NR * NR + 10> w;//西
bitset<NR * NR + 10> e;//东
bitset<NR * NR + 10> dp;//模拟数组

int main(){
    int n, m, len;
    scanf("%d%d%d", &n, &m, &len);//读入
    for(int i = 1;i <= n;i++){
        scanf("%s", a[i] + 1);
        for(int j = 1;j <= m;j++){
            if(a[i][j] == '.'){
                bs[(i - 1) * n + j] = true;//存进 bitset
            }
            //对于e和w,在位移时会出现这一行的第一个(最后一个)变成了上一行(下一行)的最后一个(第一个)的情况,需要特殊处理
            if(j != 1) e[(i - 1) * n + j] = true;
            if(j != m) w[(i - 1) * n + j] = true;
        }
    }
    scanf("%s", s + 1);
    dp = bs;
    for(int i = 1;i <= len;i++){//开始模拟
        if(s[i] == 'W'){//W方向
            dp = ((dp >> 1) & w) & bs;//特殊处理
        }
        if(s[i] == 'E'){//E方向
            dp = ((dp << 1) & e) & bs;//特殊处理
        }
        //我们发现,对于向N或向S,可以直接用右移或左移m个来表示
        if(s[i] == 'N'){//N方向
            dp = (dp >> m) & bs;
        }
        if(s[i] == 'S'){//S方向
            dp = (dp << m) & bs;
        }
        if(s[i] == '?'){//对于问号,将其拓展为 W E N S 四个方向
            dp = (((dp >> 1) & w) | ((dp << 1) & e) | (dp >> m) | (dp << m)) & bs;
        }
    }
    printf("%d\n", (int)dp.count());//答案为true的个数
    return 0;
}
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值