题解-[HNOI2001]遥控赛车比赛

前置知识:记忆化搜索、\(\texttt{Bfs}\)。

参考资料

https://www.luogu.com.cn/blog/CYJian/solution-p2226

跳转按钮

题解-[HNOI2001]遥控赛车比赛
\(\texttt{Introduction}\)
\(\texttt{Description}\)
\(\texttt{Solution}\)
\(\texttt{Code}\)

\(\texttt{Introduction}\)

蒟蒻练习历年省选题时遇见此题,\(\texttt{WA}\) 了好多发才 \(\texttt{AC}\),感到这题的巧妙足以记成题解。


\(\texttt{Description}\)

[HNOI2001]遥控赛车比赛

给你一个由 \(0\) 和 \(1\) 组成的 \(N\times M\) 地图,\(1\) 可走,\(0\) 是障碍。如果你反应力为 \(z\),那么你每次至少直走 \(z\) 步后才能转弯。起点为 \((sx,sy)\),终点为 \((tx,ty)\)。求反应力为 \(1\sim 10\) 时的最短路(如果到不了终点不输出,具体看题目链接)。

数据范围:\(1\le N,M\le 100\)。


\(\texttt{Solution}\)

这题是什么意思呢?如下图:

起点为 \((1,4)\),终点为 \((10,7)\)。

如果反应力为 \(1\),即走一步可以转个方向,那么最短路方案如图 \(2\),长度为 \(16\)。

如果反应力为 \(2\),即每直走两步可以换个方向,那么最短路方案如图 \(1\),长度为 \(18\)。


貌似很简单,做法很直接:\(\texttt{Bfs}\),记忆化搜索。

因为方向在这题中很重要,所以记录数组 \(dep_{i,j,k}\) 和 \(f_{i,j,k}\) 表示走到 \((i,j)\) 这个格子方向为 \(k\) 时在最短路条件下的路程和当前方向上直走了的距离(四个方向用 \(0,1,2,3\) 表示)

然后每次 \(\texttt{Bfs}\) 拓展下一步路径的时候,特判一下,如果方向与 \(k\) 不同,那么必须满足 \(f_{i,j,k}\ge z\)。

看起来这题就只有普及的复杂度,但是如果你把代码敲出来一交,最多得个 \(50\) 分


你会自闭很久直到找到反例——一个更令你自闭的东西:

路重复走,转 \(180^{\circ}\) 反而可能更优

如下图:

图 \(1\) 中的地图如果用你的代码只能跑反应力为 \(1\)。但是这种路径反应力为 \(2\) 可以跑!

同理,图 \(2\) 中如果走这种路重复走,转 \(180^{\circ}\) 的路径,长度可以减至 \(10\)(虚线路径为原计划路径,长度远大于 \(10\))。

那么题目貌似会变得很混乱:走过的地方也可以走,同一个位置更长的路径可能更优

然而仔细思考后会发现,只有两种情况同一个位置的路径会更优:

  1. 更短。

  2. 当前方向上直走得更长。

其中第二种更优只能在继续直走中体现(如果转弯之前直走了多长没关系)。

然后敲个带个特判的记忆化广搜即可(普及知识不多说,特判会在代码中标识)。


\(\texttt{Code}\)

#include <bits/stdc++.h>
using namespace std; //&Start
#define lng long long
#define lit long double
#define kk(i,n) "\n "[i<n]
const int inf=0x3f3f3f3f;
const lng Inf=1e17; //&Debug
int open(0);
#define Debug if(open) //&Data
const int N=110;
int n,m,maze[N][N],fx[4]={0,0,-1,1},fy[4]={-1,1,0,0};
struct Marco{int x,y,k;}s,t; //Bfs 状态 //&Bfs
const int Q=5e6+10;
int dep[N][N][4],f[N][N][4],ql,qr;
Marco q[Q];
int ok(int x,int y){return x>=1&&x<=n&&y>=1&&y<=m&&maze[x][y];}
int bfs(int z){
Debug printf("---%d---\n",z);//###
if(!ok(s.x,s.y)||!ok(t.x,t.y)) return -1;
memset(dep,0x3f,sizeof dep),memset(f,0,sizeof f);
int *D=dep[s.x][s.y],*F=f[s.x][s.y]; Marco tp;
D[0]=D[1]=D[2]=D[3]=F[0]=F[1]=F[2]=F[3]=0;//初始化起点
ql=1,qr=0,q[++qr]=(s.k=0,s),q[++qr]=(s.k=1,s),q[++qr]=(s.k=2,s),q[++qr]=(s.k=3,s);//起点可以是任意方向
while(qr>=ql){ //手模队列
tp=q[ql++];
Debug printf("%d %d %d\n",tp.x,tp.y,tp.k);//###
D=dep[tp.x][tp.y],F=f[tp.x][tp.y];
if(tp.x==t.x&&tp.y==t.y) return D[tp.k];//找到出口了!
for(int k=0;k<4;k++){
int xt=tp.x+fx[k],yt=tp.y+fy[k];
if(ok(xt,yt)&&(D[tp.k]+1<dep[xt][yt][k]||F[tp.k]>=f[xt][yt][k])){//特判:两种更优
if(k==tp.k){
dep[xt][yt][k]=D[tp.k]+1;//普通Bfs拓展
f[xt][yt][k]=F[tp.k]+1;
q[++qr]=(Marco){xt,yt,k};
} else if(F[tp.k]>=z&&D[tp.k]+1<dep[xt][yt][k]){//特判:要转弯即使直走得更长也没用
dep[xt][yt][k]=D[tp.k]+1;
f[xt][yt][k]=1;
q[++qr]=(Marco){xt,yt,k};
}
}
}
}
return -1;//被困住了,到不了出口
} //&Main
int main(){
scanf("%d%d",&n,&m);
scanf("%d%d%d%d",&s.x,&s.y,&t.x,&t.y);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",maze[i]+j);
for(int i=1,tp;i<=10;i++)
if((tp=bfs(i))!=-1) printf("%d %d\n",i,tp);
else break;
return 0;
}

我还是太蒻了。祝大家学习愉快!

题解-[HNOI2001]遥控赛车比赛的更多相关文章

  1. 【洛谷 P2226】 [HNOI2001]遥控赛车比赛(最短路)

    题目链接 首先拆点,把每个点拆成4个点,表示到达这个点的时候赛车的朝向. 然后考虑连边. 相邻同向并且都是可以走的点直接连边权1的边. 至于怎么转向,只需在每个点\(i\)向每个方向一直拓展直到不能走 ...

  2. 洛谷 题解 UVA12661 【有趣的赛车比赛 Funny Car Racing】

    [题意] 在一个赛车比赛中,赛道有\(n(n<=300)\)个交叉点和\(m(m<=50000)\)条单向道路.有趣的是,每条道路都是周期性关闭的.每条道路用5个整数\(u,v,a,b,t ...

  3. 【水水水】【洛谷 U4566】赛车比赛

    题目背景 kkk在赛车~ 题目描述 现在有N辆赛车行驶在一条直线跑道(你可以认为跑道无限长)上.它们各自以某种速度匀速前进,如果有两辆车A车和B车,A车在B车的后面,且A车的速度大于B车的速度,那么经 ...

  4. 赛车比赛(洛谷U4566)

    题目背景 kkk在赛车~ 题目描述 现在有N辆赛车行驶在一条直线跑道(你可以认为跑道无限长)上.它们各自以某种速度匀速前进,如果有两辆车A车和B车,A车在B车的后面,且A车的速度大于B车的速度,那么经 ...

  5. UVA 12661 Funny Car Racing 有趣的赛车比赛(最短路,变形)

    题意:赛道有n个交叉点,和m条单向路径(有重边),每条路都是周期性关闭的,且通过仍需一段时间.在比赛开始时,所有道路刚好打开,选择进入该道路必须满足“在打开的时间段进入,在关闭之前出来”,即不可在路上 ...

  6. 【题解】HNOI2013比赛

    [题解][P3230 HNOI2013]比赛 将得分的序列化成样例给的那种表格,发现一行和一列是同时确定的.这个表格之前是正方形的,后来长宽都减去一,还是正方形.问题形式是递归的.这就启示我们可以把这 ...

  7. BZOJ3190[JLOI2013]赛车

    Description 这里有一辆赛车比赛正在进行,赛场上一共有N辆车,分别称为个g1,g2--gn.赛道是一条无限长的直线.最初,gi位于距离起跑线前进ki的位置.比赛开始后,车辆gi将会以vi单位 ...

  8. BZOJ 3190 赛车 | 计算几何

    BZOJ 3190 赛车 题面 这里有一辆赛车比赛正在进行,赛场上一共有N辆车,分别称为个g1,g2--gn.赛道是一条无限长的直线.最初,gi位于距离起跑线前进ki的位置.比赛开始后,车辆gi将会以 ...

  9. OI队测题解:

    Test 17   T1: 题目大意: 喵星系有n个星球,标号为1到n,星球以及星球间的航线形成一棵树. 所有星球间的双向航线的长度都为1.小昕要在若干个星球建矿石仓库,设立每个仓库的费用为K.对于未 ...

随机推荐

  1. linux配置和查看环境变量

    环境变量一般是指在操作系统中用来指定操作系统运行环境的一些参数,比如临时文件夹位置和系统文件夹位置等等. 一.Linux的变量种类 按变量的生存周期来划分,Linux变量可分为两类: 1.永久的:需要 ...

  2. VB6实现Excel多工作簿数据合并

    以前的同事,工作需要,让我帮忙完成多个工作簿的汇总. 我就用最熟悉的VB6写了一个Form应用程序,这是因为我不知道她目前的系统和Office情况,如果太高大上了,她不会部署安装.索性就简单粗暴地来个 ...

  3. 倾斜摄影数据转cesium 3D tiles工具介绍

    软件操作:http://jingyan.baidu.com/article/3aed632e3912c8701080912c.html 软件测试数据: 数据,油库链接: http://pan.baid ...

  4. kettle的基本使用

    一.下载下载kettlehttp://sourceforge.net/projects/pentaho/files/Data%20Integration/7.0/pdi-ce-7.0.0.0-25.z ...

  5. 点分治——POJ 1741

    写的第一道点分治的题目,权当认识点分治了. 点分治,就是对每条过某个点的路径进行考虑,若路径不经过此点,则可以对其子树进行考虑. 具体可以看menci的blog:点分治 来看一道例题:POJ 1741 ...

  6. [LC] 256. Paint House

    There are a row of n houses, each house can be painted with one of the three colors: red, blue or gr ...

  7. C# 内置的类型转换方法

    C# 提供了下列内置的类型转换方法: 序号 方法 & 描述 1 ToBoolean把类型转换为布尔型. 2 ToByte把类型转换为字节类型. 3 ToChar如果可能的话,把类型转换为单个 ...

  8. [LC] 257. Binary Tree Paths

    Given a binary tree, return all root-to-leaf paths. Note: A leaf is a node with no children. Example ...

  9. Ionic 4 beta + Capacitor beta 尝鲜

    本文为原创文章,转载请标明出处 开发环境: Ionic 4 beta 3.Capacitor beta 6. 首先 ionic start 工程名 blank --type=angular,问是否集成 ...

  10. Junit 单元测试在 intelliJ IDEA 中的安装

    1.为什么使用Junit我们都知道,main 方法是一个程序的入口,通常来说,没有main方法,程序就无法运行.我们经常会写一些class文件(如下图所示),他们并没有自己的main方法.那么我们如何 ...