P6474 [NOI Online #2 入门组] 荆轲刺秦王

bfs+差分+卡常

本来我其实是场内选手,但是因为记错提交时间,晚了半小时才交,交不上了,就自动降级为了场外选手

题面复杂,不简述了


首先定义状态 \(dis(x,y,num1,num2)\) 表示当前坐标是 \((x,y)\),然后用了 \(num1\) 次隐身,\(num2\) 次瞬移,的最短时间

答案就是 \(\min(dis(tx,ty,[0,c1],[0,c2]))\),其中 \((tx,ty)\) 为终点坐标

对于每个坐标,枚举 12 个方向

分别是 8 个正常走路的方向,如果进入了卫兵监视范围,\(num1\) 要加一,如果走到了卫兵的格子上,就不行

还有 4 个是瞬移的方向,此时 \(num2\) 要加一

同样要考虑是否进入卫兵监视范围和是否走到了卫兵的格子上

如果发现走到了最终格子,更新答案

题目要求先最小化时间,再最小化技能次数和,再最小化隐身次数,按要求更新就好,稍显麻烦

因此,也不能一搜到答案就退出,因为退出以后,后面可能还有花费同样时间,但技能次数小的方案

如果搜到就直接退出了后面的就取不到了

那么复杂度显然是 \(O(n\times m\times c1\times c2)\),但肯定远远跑不满

考虑怎么标记卫兵的防护范围,因为是曼哈顿距离,所以手动标记一下会观察出,防护范围就是一个斜着的正方形

比较像飞飞侠这题里的每一步能走哪些格的描述,当然做法上和这个题没有任何关系

直接标记是 \(n^4\),不行

因为是一个区间,很容易想到使用线段树,对每一行建线段树,然后区间修改,bfs 时单点查询

但是这样多一个 \(\log\),变成了 \(O(n\times m\times c1\times c2\times 12\times \log m)\approx 2\cdot 10^9\),即使跑不满也够呛能过

然后事实证明,跑的是非常不满,只 TLE 了一个点,下面介绍的差分做法在优化之前也是 TLE 一个

所以因为查询是单点,所以可以使用差分

比如要修改区间 \([x,y]\),让他们区间加一,设差分数组为 \(t\),则让 \(t_x\) 加一,\(t_{y+1}\) 减一

然后,在对 \(t\) 做一个前缀和,就是原数组

为什么?因为 \(t_i\) 再做前缀和之前,表示的实际上是:\(i\) 号位置的数,比 \(i-1\) 位置的数大了多少

\(t_x\) 加一,就是 \(x\) 位置在经过区间加以后,比 \(x-1\) 位置的数,又多大了 \(1\)

而 \([x+1,y]\) 之间的所有位置,并 没有比它前面那个数多大或少大(相对大小没变),所以不用改变

而 \(t_{y+1}\) 减一,就是因为 \([x,y]\) 区间加以后,\(y+1\) 位置的数比 \(y\) 位置的数 少大了 \(1\)

那么,\(0\) 号位置是 \(0\),所以 \(1\) 号位置比它“多大了”多少,那么 \(1\) 号位置就是多少

\(1\) 号位置确定了,\(2\) 号位置比 \(1\) 号位置“多大了”多少(在这里是“多大了”\(t_2\)),那 \(2\) 号位置实际的值就是 \(t_1+t_2\),因为 \(t_1\) 在这里是相当于已经做过前缀和了,表示实际数值

然后 \(t_2\) 也就表示实际数值了,就一路前缀和到后面,算出了真实值

卡常

不要用STL!!!

队列一定不要用STL的,慢到死,会被卡在第 18 个点

还有,如果状态有很多维(比如这个题就是),不要偷懒把状态一个一个入队(我就经常这么做),太慢,最好是构建一个结构体

以前做的题不卡常看不出来

STL队列本机 10s+,改成手写本机+结构体 3s 多,但洛谷上还是过不了

然后加一个最优性剪枝

if(step>ans) continue;

就是如果当前的步数已经大于当前记录下的最优答案了,就不再继续搜了

然后本机 1s,洛谷 3s

代码写的丑了,挺长

#include<cstdio>
#include<algorithm>
#include<queue>
#include<iostream>
#include<cmath>
#include<map>
#include<iomanip>
#include<cstring>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
register int x=0;register int y=1;
register char c=std::getchar();
while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
return y?x:-x;
}
int n,m,d,c1,c2;
int sx,sy,tx,ty;
int have[355][355],vis[355][355][17][17];
const int dx[8]={-1,-1,-1,0,0,1,1,1};
const int dy[8]={-1,0,1,-1,1,-1,0,1};
const int dxx[4]={-1,1,0,0};
const int dyy[4]={0,0,1,-1};
int t[355][355];
int ans=1e9,ans_num1,ans_num2;
inline int abs(int x){return x>0?x:-x;}
inline int min(int x,int y){return x>y?y:x;}
inline int max(int x,int y){return x>y?x:y;}
struct data{
int x,y,step,num1,num2;
}q[30000006];
inline int bfs(){
reg int head=1,tail=0;
q[++tail]=(data){sx,sy,0,0,0};
vis[sx][sy][0][0]=1;
reg int x,y,step,num1,num2;
data data_;
while(head<=tail){
data_=q[head++];
x=data_.x;
y=data_.y;
step=data_.step;
num1=data_.num1;
num2=data_.num2;
if(step>ans) continue;
for(reg int x_,y_,k=0;k<8;k++){
x_=x+dx[k];y_=y+dy[k];
if(x_<1||x_>m) continue;
if(y_<1||y_>n) continue;
if(have[x_][y_]) continue;
if(t[y_][x_]){
if(num1==c1) continue;
if(vis[x_][y_][num1+1][num2]) continue;
if(x_==tx&&y_==ty){
if(step+1<ans) ans=step+1,ans_num1=num1+1,ans_num2=num2;
else if(step+1==ans){
if(num1+num2+1<ans_num1+ans_num2) ans_num1=num1+1,ans_num2=num2;
else if(num1+num2+1==ans_num1+ans_num2){
if(num1+1<ans_num1) ans_num1=num1+1,ans_num2=num2;
}
}
}
vis[x_][y_][num1+1][num2]=1;
q[++tail]=(data){x_,y_,step+1,num1+1,num2};
}
else{
if(vis[x_][y_][num1][num2]) continue;
if(x_==tx&&y_==ty){
if(step+1<ans) ans=step+1,ans_num1=num1,ans_num2=num2;
else if(step+1==ans){
if(num1+num2<ans_num1+ans_num2) ans_num1=num1,ans_num2=num2;
else if(num1+num2==ans_num1+ans_num2){
if(num1<ans_num1) ans_num1=num1,ans_num2=num2;
}
}
}
vis[x_][y_][num1][num2]=1;
q[++tail]=(data){x_,y_,step+1,num1,num2};
}
}
if(num2==c2) continue;
for(reg int x_,y_,k=0;k<4;k++){
x_=x+dxx[k]*d;y_=y+dyy[k]*d;
if(x_<1||x_>m) continue;
if(y_<1||y_>n) continue;
if(have[x_][y_]) continue;
if(t[y_][x_]){
if(num1==c1) continue;
if(vis[x_][y_][num1+1][num2+1]) continue;
if(x_==tx&&y_==ty){
if(step+1<ans) ans=step+1,ans_num1=num1+1,ans_num2=num2+1;
else if(step+1==ans){
if(num1+num2+2<ans_num1+ans_num2) ans_num1=num1+1,ans_num2=num2+1;
else if(num1+num2+2==ans_num1+ans_num2){
if(num1+1<ans_num1) ans_num1=num1+1,ans_num2=num2+1;
}
}
}
vis[x_][y_][num1+1][num2+1]=1;
q[++tail]=(data){x_,y_,step+1,num1+1,num2+1};
}
else{
if(vis[x_][y_][num1][num2+1]) continue;
if(x_==tx&&y_==ty){
if(step+1<ans) ans=step+1,ans_num1=num1,ans_num2=num2+1;
else if(step+1==ans){
if(num1+num2+1<ans_num1+ans_num2) ans_num1=num1,ans_num2=num2+1;
else if(num1+num2+1==ans_num1+ans_num2){
if(num1<ans_num1) ans_num1=num1,ans_num2=num2+1;
}
}
}
vis[x_][y_][num1][num2+1]=1;
q[++tail]=(data){x_,y_,step+1,num1,num2+1};
}
}
}
return -1;
}
int main(){
// std::freopen("bandit18.in","r",stdin);
// std::freopen("bandit.out","w",stdout);
n=read();m=read();c1=read();c2=read();d=read();
reg char c;
for(reg int i=1;i<=n;i++){
for(reg int j=1;j<=m;j++){
c=std::getchar();
while(c!='S'&&c!='T'&&c!='.'&&(c<'0'||c>'9')) c=std::getchar();
if(c=='S') sx=j,sy=i;
else if(c=='T') tx=j,ty=i;
else if(c>='0'&&c<='9'){
reg int x=c^48;c=std::getchar();
while(c>='0'&&c<='9') x=x*10+(c^48),c=std::getchar();
have[j][i]=1;
int tmp=std::min(n,i+x-1);
for(reg int k=std::max(i-x+1,1);k<=tmp;k++)
t[k][max(1,j-x+1+abs(i-k))]++,t[k][min(m,j+x-1-abs(i-k))+1]--;
}
}
}
for(reg int i=1;i<=n;i++)
for(reg int j=1;j<=m;j++) t[i][j]+=t[i][j-1];
bfs();
if(ans==1e9) std::puts("-1");
else std::printf("%d %d %d",ans,ans_num1,ans_num2);
return 0;
}

P6474 [NOI Online #2 入门组] 荆轲刺秦王的更多相关文章

  1. P7473 [NOI Online 2021 入门组] 重力球

    P7473 [NOI Online 2021 入门组] 重力球 题意 给你一个正方形平面,某些位置有障碍,对于平面上两个球,每次你可以改变重力方向使两个球下落到最底端,求使两个球位置重合的最小改变重力 ...

  2. 洛谷 P6189 - [NOI Online #1 入门组]跑步(根号分治+背包)

    题面传送门 题意: 求有多少个数列 \(x\) 满足: \(\sum x_i=n\) \(x_i\geq x_{i+1}\) 答案对 \(p\) 取模. ...你确定这叫"入门"组 ...

  3. NOI Online 2021 入门组 T1

    Description 题目描述 Alice.Bob 和 Cindy 三个好朋友得到了一个圆形蛋糕,他们打算分享这个蛋糕. 三个人的需求量分别为 \(a, b, c\),现在请你帮他们切蛋糕,规则如下 ...

  4. [NOI 2020 Online] 入门组T1 文具采购(洛谷 P6188)题解

    原题传送门 题目部分:(来自于考试题面,经整理) [题目描述] 小明的班上共有 n 元班费,同学们准备使用班费集体购买 3 种物品: 1.圆规,每个 7 元. 2.笔,每支 4 元. 3.笔记本,每本 ...

  5. NOI Online #1 入门组 魔法

    全网都是矩阵快速幂,我只会倍增DP 其实这题与 AcWing 345. 牛站 还是比较像的,那题可以矩阵快速幂 / 倍增,这题也行. 先 \(Floyd\) 预处理两点之间不用魔法最短距离 \(d_{ ...

  6. [题解] [NOI Online 2021 入门组 T3] 重力球

    题目大意 在一个 \(n\times n\) 的矩形中,题目会给出 \(m\) 个障碍物.有两个小球,你可以选定四个方向(上下左右)的其中一个,小球会朝着这四个方向一直滚动,直到遇到障碍物或是矩形的边 ...

  7. P6189 [NOI Online #1 入门组] 跑步 (DP/根号分治)

    (才了解到根号分治这样的妙方法......) 将每个数当成一种物品,最终要凑成n,这就是一个完全背包问题,复杂度O(n2),可以得80分(在考场上貌似足够了......) 1 #include < ...

  8. 【NOI Online 2020】入门组 总结&&反思

    前言: 这次的NOI Online 2020 入门组我真的无力吐槽CCF的网站了,放段自己写的diss的文章,供一乐 如下:(考试后当天晚上有感而发) 今天是个好日子!!!(我都经历了什么...... ...

  9. NOI ONLINE 入门组 魔法 矩阵快速幂

    做了这道题我才发现NOI入门组!=NOIP普及组 题目链接 https://www.luogu.com.cn/problem/P6190 题意 给出一张有向图,你有K次机会可以反转一条边的边权,即让它 ...

随机推荐

  1. Loop Unrolling 循环展开

    在csapp第五章5.2中提到了循环展开(loop unrolling).这里展开一下为什么循环展开可以提升程序的效率. 以书中计算数组和的两段代码为例: 1.未展开: void psum1(floa ...

  2. matplotlib中的基本概念

    有外语基础的朋友看这里: matplotlib官方文档 Figure(图像): 组成部分

  3. windows上jmeter目录结构功能

    1.bin :存储了jmeter的可执行程序,如启动 2.lib:存储了jmeter的整合的功能(如.jar文件程序) 3.启动jmeter:双击bin\apachejmeter.jar jmeter ...

  4. 【FreeMarker】【程序开发】数据模型,对象包装

    [FreeMarker][程序开发]数据模型,对象包装 分类: Java.FreeMarker2014-10-25 18:49 413人阅读 评论(0) 收藏 举报 FreeMarker   目录(? ...

  5. AJ学IOS 之微博项目实战(3)微博主框架-UIImage防止iOS7之后自动渲染_定义分类

    AJ分享,必须精品 一:效果对比 当我们设置tabBarController的tabBarItem.image的时候,默认情况下会出现图片变成蓝色的效果,这是因为ios7之后会对图片自动渲染成蓝色 代 ...

  6. 05-移动web之流式布局

    一.视口 1.常见屏幕知识 设备 解释 描述 宽 屏幕的宽度 - (单位:英寸) 屏幕的宽度 高 屏幕的高度 -(单位:英寸) 屏幕的高度 对角线 屏幕的对角线的长度 英寸 一般说手机尺寸 是指以屏幕 ...

  7. 用python为喜欢的人写一个程序,每天发送贴心的消息

    消息内容 包括如下: 日期(阳历+阴历): 每日壹句(内容来自爱词霸[1]): 天气预报(内容来自中国天气网[2]): 天气情况: 温度情况: 穿衣指数: 减肥指数: 空气指数: 紫外线指数: 消息效 ...

  8. 关于Python+selenium 定位浏览器弹窗元素

    首先要确定弹窗的类型: (1)div弹窗 (2)新标签页弹窗 (3)alert弹窗 一,div弹窗div弹窗是浏览器中比较好定位的弹窗,定位的方法与普通的元素一样.不过这里会有一个坑,明明可以找到这个 ...

  9. mybatis源码配置文件解析之二:解析settings标签

    在前边的博客中分析了mybatis解析properties标签,<mybatis源码配置文件解析之一:解析properties标签>.下面来看解析settings标签的过程. 一.概述 在 ...

  10. Mark down 使用总结

    Markdown语法 Markdown是一种纯文本.轻量级的标记语言,通过简单的标记,就可以使文本具有一定的格式,操作简单.使用广泛,常见的比如github上的README.md . Markdown ...