P6226 [BalticOI 2019] 潜艇 / Nautilus 题解
P6226 [BalticOI 2019] 潜艇 / Nautilus 题解
貌似是校内模拟赛第一道场切的青?写篇题解祭下。
其实是听到一车同学都过了后才去冲正解的……
下文用 \(c_{i,j}\) 表示地图第 \(i\) 行第 \(j\) 列,\(s_i\) 表示信号的第 \(i\) 个字符。
对于子任务一,直接 \(O(RCM)\) 的暴力枚举就好了,而且大概率跑不满。
对于子任务二,考虑 DP。设 \(f_{i,j,k}\) 表示走完第 \(k\) 步,第 \(i\) 行第 \(j\) 列是否可成为终点。
首先考虑 \(k=0\) 的情况。因为没有限制,所以只要 \(c_{i,j}\) 不是 # 就可以了。
然后考虑 \(k\neq 0\) 的情况。
首先 \(f_{i,j,k}\) 肯定是由 \(f_{a,b,k-1}\) 推来的,至于 \(a\) 和 \(b\) 是什么具体看 \(s_k\)。
然后来分讨,根据当前位置和移动信息反推上一步的位置。
如果 \(c_{i,j}\) 是岩石则无论如何都不行。
如果 \(s_k\) 是 N,那么 \(f_{i,j,k}\) 由 \(f_{i+1,j,k-1}\) 推来。
如果 \(s_k\) 是 S,那么 \(f_{i,j,k}\) 由 \(f_{i-1,j,k-1}\) 推来。
如果 \(s_k\) 是 W,那么 \(f_{i,j,k}\) 由 \(f_{i,j+1,k-1}\) 推来。
如果 \(s_k\) 是 E,那么 \(f_{i,j,k}\) 由 \(f_{i,j-1,k-1}\) 推来。
如果 \(s_k\) 是 ?,则可以从四个方向任意挑选符合条件的。
注意边界问题。
放个代码:
for(int k=0,o,t;k<=l;++k)
{
for(int i=1;i<=n;++i)
{
for(int j=1;j<=m;++j)
{
if(ch[i][j]=='#'){f[i][j][k]=0;continue;}
if(!k){f[i][j][k]=1;continue;}
if(s[k]=='S'&&i-1>0)f[i][j][k]=f[i-1][j][k-1];
if(s[k]=='W'&&j+1<=m)f[i][j][k]=f[i][j+1][k-1];
if(s[k]=='N'&&i+1<=n)f[i][j][k]=f[i+1][j][k-1];
if(s[k]=='E'&&j-1>0)f[i][j][k]=f[i][j-1][k-1];
if(s[k]=='?')
{
if(i-1>0)f[i][j][k]|=f[i-1][j][k-1];
if(j+1<=m)f[i][j][k]|=f[i][j+1][k-1];
if(i+1<=n)f[i][j][k]|=f[i+1][j][k-1];
if(j-1>0)f[i][j][k]|=f[i][j-1][k-1];
}
}
}
}
好的恭喜你现在已经有了 \(66\) 分。
接下来考虑第三个子任务。
然后发现 \(f_{i,j,k}\) 的转移只和 \(f_{a,b,k-1}\) 有关,所以考虑滚动数组,滚掉 \(k\) 的那一维。
不会的请出门右转。
然后我赛时因为滚动完没清空被硬控十分钟,还是太菜了。
接下来观察代码。
发现空间没问题,时间略超,DP 数组全是 bool 类型的变量。
再联想这次是数据结构专题。
很自然想到用 bitset 压缩时间。
具体地,把 \(f_{i,j}\) 压缩成 \(f_i\)。
然后考虑转移。
\(s_k\) 为 N 或 S 的很简单,只要把 \(i\) 改一改就好了。
那么要修改 \(i\) 的每一位呢?
考虑左移和右移操作。
重点:bitset 的下标是从右往左编号,而非从左往右!
所以如果本来是 \(j-1\) 的操作,要变成左移一位,\(j+1\) 的操作则是右移一位。
然后我们发现漏了岩石的情况。毕竟不可能一位一位判断。
此时要充分利用人类智慧,考虑将原图的每一行也压成 bitset,其中岩石为 \(0\),水为 \(1\),第 \(i\) 行的情况设为 \(g_i\)。
然后在每个 \(f_i\) 算完后按位与上 \(g_i\)。就可以排除岩石的干扰。
这为什么是对的呢?
我们继续分讨。
如果 \(g_{i,j}\) 为 \(0\),那么按位与上 \(f_{i,j}\) 一定为 \(0\),对应不合法情况。
如果 \(g_{i,j}\) 为 \(1\),那么按位与上 \(f_{i,j}\) 结果不变,不影响答案。
证毕。
然后时间复杂度是 \(O(\frac{RCM}{\omega})\),其中 \(\omega\) 为 \(32\)。刚好能卡过。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ljl;
const int N=505;
int n,m,l,ans;
char ch[N][N];
string s;
bitset<N> f[2][N],g[N];
int main(){
ios::sync_with_stdio(0);
cin>>n>>m>>l;
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
cin>>ch[i][j];
cin>>s;s=" "+s;
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
f[0][i][j]=(ch[i][j]=='#'?0:1);
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
g[i][j]=(ch[i][j]=='#'?0:1);
for(int k=1,o,t;k<=l;++k)
{
o=k&1;t=o^1;
for(int i=1;i<=n;++i)f[o][i].reset();
if(s[k]=='S')//i-1>0
{
for(int i=2;i<=n;++i)
f[o][i]=f[t][i-1];
// for(int i=2;i<=n;++i)
// {
// for(int j=1;j<=m;++j)
// {
// f[o][i][j]=f[t][i-1][j];
// }
// }
}
if(s[k]=='N')
{
for(int i=1;i<n;++i)f[o][i]=f[t][i+1];
// for(int i=1;i<n;++i)
// {
// for(int j=1;j<=m;++j)
// {
// f[o][i][j]=f[t][i+1][j];
// }
// }
}
if(s[k]=='W')//j<m
{
for(int i=1;i<=n;++i)
f[o][i]=f[t][i]>>1;
// for(int i=1;i<=n;++i)
// {
// for(int j=1;j<m;++j)
// {
// f[o][i][j]=f[t][i][j+1];
// }
// }
}
if(s[k]=='E')//j>1
{
for(int i=1;i<=n;++i)
f[o][i]=(f[t][i]<<1);
// for(int i=1;i<=n;++i)
// {
// for(int j=2;j<=m;++j)
// {
// f[o][i][j]=f[t][i][j-1];
// }
// }
}
if(s[k]=='?')
{
for(int i=2;i<=n;++i)
f[o][i]|=f[t][i-1];
for(int i=1;i<=n;++i)
f[o][i]|=f[t][i]<<1;
for(int i=1;i<n;++i)
f[o][i]|=f[t][i+1];
for(int i=1;i<=n;++i)
f[o][i]|=f[t][i]>>1;
}
for(int i=1;i<=n;++i)f[o][i]&=g[i];
}
for(int i=1;i<=n;++i)
ans+=f[l&1][i].count();
cout<<ans<<'\n';
return 0;
}

浙公网安备 33010602011771号