超级详解洛谷P4011 孤岛营救问题(bfs超难题)(保证看懂)

news2025/1/12 22:47:40

题目

说明

1944 年,特种兵麦克接到国防部的命令,要求立即赶赴太平洋上的一个孤岛,营救被敌军俘虏的大兵瑞恩。瑞恩被关押在一个迷宫里,迷宫地形复杂,但幸好麦克得到了迷宫的地形图。迷宫的外形是一个长方形,其南北方向被划分为N 行,东西方向被划分为M列,于是整个迷宫被划分为 N×M 个单元。每一个单元的位置可用一个有序数对(单元的行号,单元的列号)来表示。南北或东西方向相邻的 2 个单元之间可能互通,也可能有一扇锁着的门,或者是一堵不可逾越的墙。迷宫中有一些单啊、元存放着钥匙,并且所有的门被分成 P类,打开同一类的门的钥匙相同,不同类门的钥匙不同。

大兵瑞恩被关押在迷宫的东南角,即(N,M)单元里,并已经昏迷。迷宫只有一个入口,在西北角。也就是说,麦克可以直接进入(1,1)单元。另外,麦克从一个单元移动到另一个相邻单元的时间为1,拿取所在单元的钥匙的时间以及用钥匙开门的时间可忽略不计。

试设计一个算法,帮助麦克以最快的方式到达瑞恩所在单元,营救大兵瑞恩。

输入格式

第 1行有 3个整数,分别表示N,M,P的值。[均小于等于10]

第 2 行是1个整数 K[小于等于150],表示迷宫中门和墙的总数。

第 I+2 行(1<=I<=K) ,有 5 个整数,依次为Xi1,Yi1,Xi2,Yi2,Gi: 当Gi>=1时,表示(Xi1,Yi1)单元与(Xi2,Yi2)单元之间有一扇第Gi类的门,当 Gi=0时,表示(Xi1,Yi1)单元与(Xi2,Yi2)单元之间有一堵不可逾越的墙(其中,|Xi1-Xi2|+|Yi1-Yi2|=1,0<=Gi<=P) 。

第K+3行是一个整数S,表示迷宫中存放的钥匙总数。

第K+3+J 行(1<=J<=S),有3个整数, 依次为Xi1,Yi1,Qi: 表示第J 把钥匙存放在(Xi1,Yi1)单元里,并且第J 把钥匙是用来开启第Qi类门的。 (其中 1<=Qi<=P) 。

输入数据中同一行各相邻整数之间用一个空格分隔。

输出格式

程序运行结束时,将麦克营救到大兵瑞恩的最短时间的值输出。如果问题无解,则输出-1。

样例

输入数据 1

4 4 9

9

1 2 1 3 2

1 2 2 2 0

2 1 2 2 0

2 1 3 1 0

2 3 3 3 0

2 4 3 4 1

3 2 3 3 0

3 3 4 3 0

4 3 4 4 0

2

2 1 2

4 2 1

输出数据 1

14

提示

存在一个格子有多把钥匙的情况


思路

此题要用状态压缩+BFS

我们可以状态压缩钥匙

vis[x][y][S] 表示在(x,y)这个位置持有钥匙状态S是否来过

BFS队列里存4个变量

分别为当前横纵坐标步数以及钥匙状态

map[x1][y1][x2][y2]表示两个点之间是否有墙或者是门。

ys[x][y][i]表示(x,y)这个点放的钥匙的种类

num[x][y]表示当前点钥匙数目

坑点:

1.钥匙不是用了就没了((#`O′)喂,没了才有问题吧

2.一个点可以放多个钥匙(人家又没说只放一个。

3.初始点可以放钥匙

4.别忘了输出-1....

那么该怎么状态压缩呢?

什么是状态压缩?

状态压缩通俗的说就是把一个不可很难直接标记的状态变得更好标记。

(通过局部状态来枚举全局状态)

如何状态压缩?

以本题为例,题目当中钥匙可能有10种,按照我们之前的方法,需要开一个10维布尔数组来进行标记,但这无疑为判重带来了巨大的麻烦。于是我们观察每把钥匙的状态,发现钥匙只有两种状态还是没有。于是考虑二进制。用一个10位二进制数来表示每一把钥匙的状态,如0000000000就表示目前你一把钥匙都没有,而0000001000就表示你有第4把钥匙,那我们如何实现呢?

首先你先确保你明白二进制的运算后再看下文。

假如我们现在一把钥匙都没有,来到了一个有第4把钥匙风水宝地,我们就需要拿起这一把钥匙,使得二进制数0000000000变成0000001000,我们就只需要把数字1向左移动3个单位,下面画个图进行理解:

也就是说如果我们取了q 把钥匙,我们需要把它压入状态只需要将它向左移动 q−1 位。

那么如果说我们有多把钥匙呢?

那我们就考虑按位或,举个例子,如果你现在有了第三把钥匙,你又拿到了第四把,那么我们只需要把这两个二进制数按位或一下即可。

画个图:

也就是说我们将两把钥匙合并只需要按位或一下即可。

例如bfs的初始化的状态压缩的代码如下:

//初始化
  int t = 0;
  for(int i = 1; i <= num[1][1]; i++) t |= (1<<(ys[1][1][i] - 1));
//t是原来的钥匙状态,1<<(ys[1][1][i] - 1)是新来的。
  vis[1][1][t] = 1;

那么我们现在解决了拿钥匙的问题,现在我们要解决判重的问题,怎么解决呢?

这次我们用按位与,举个例子,如果你现在有了第2,3,4,7,9把钥匙,而这扇门需要第八把钥匙,我们只需要把你手中有的钥匙的状态第八把钥匙的状态按位与,如果结果为0,则没有这一把钥匙,否则就这把钥匙,画个图:

 

 

 但是你要注意,第 p 把钥匙的状态时二进制数左移 p−1 位的,不能搞错。

例如bfs中判重的代码:

for(int j = 1; j <= num[t.x][t.y]; j++)  cosx |= (1 << (ys[t.x][t.y][j] - 1));
if(vis[t.x][t.y][cosx]) continue;

还有判断目前移到的门拿没拿到钥匙:

int p;
if((p = mp[d.x][d.y][t.x][t.y]) != 0)
  if((d.cos & (1 << (p - 1))) == 0)
    continue;

这道题目好像难的就是状态压缩吧,其他你应该可以自己做出来的。


代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
int n,m,p,k,s;
//迷宫有n行m列  迷宫里所有的门被分成p类  迷宫中门和墙的总数为k  迷宫中存放的钥匙总数为s
int num[20][20];//表示(x,y)这个点钥匙数量
int ys[20][20][10001];//表示(x,y)这个点放的钥匙
bool vis[20][20][10001];//vis[x][y][s]表示在(x,y)这个位置持有钥匙状态为s是否来过
int mp[20][20][20][20];//mp[x1][y1][x2][y2]表示两个点直接是否有墙或者是门
struct node
{
  int x,y,step,cos;//x,y,从起点到(x,y)的路径长度,钥匙状态
  node()
  {
    x = y = step = cos = 0;
  }
};
queue<node>q;
int dx[4] = {-1,0,1,0},dy[4] = {0,-1,0,1};
int bfs()
{
  //初始化
  int t = 0;
  for(int i = 1; i <= num[1][1]; i++) t |= (1<<(ys[1][1][i] - 1));
  vis[1][1][t] = 1;
  node d;
  d.x = 1;
  d.y = 1;
  d.step = 0;
  d.cos = t;
  q.push(d);
  while(!q.empty())
  {
    node d = q.front();
    q.pop();
    if(d.x == n && d.y == m) return d.step;
    for(int i = 0; i < 4; i++)
    {
      node t;
      t.x = d.x + dx[i];
      t.y = d.y + dy[i];
      if(t.x > 0 && t.x <= n && t.y > 0 && t.y <= m && mp[d.x][d.y][t.x][t.y] != -1)
      {
        int p;
        if((p = mp[d.x][d.y][t.x][t.y]) != 0)
          if((d.cos & (1 << (p - 1))) == 0)
            continue;
        int cosx = d.cos;
        for(int j = 1; j <= num[t.x][t.y]; j++)
          cosx |= (1 << (ys[t.x][t.y][j] - 1));
        if(vis[t.x][t.y][cosx]) continue;
        vis[t.x][t.y][cosx] = 1;
        t.step = d.step + 1;
        t.cos = cosx;
        q.push(t);
      }
    }
  }
  return -1;
}
signed main()
{
  scanf("%lld%lld%lld%lld",&n,&m,&p,&k);
  for(int i = 1; i <= k; i++)
  {
    int x_1,x_2,y_1,y_2,g;
    scanf("%lld%lld%lld%lld%lld",&x_1,&y_1,&x_2,&y_2,&g);
    if(g == 0) mp[x_1][y_1][x_2][y_2] = mp[x_2][y_2][x_1][y_1] = -1;
    else mp[x_1][y_1][x_2][y_2] = mp[x_2][y_2][x_1][y_1] = g;
  }
  scanf("%lld",&s);
  for(int i = 1; i <= s; i++)
  {
    int x,y,q;
    scanf("%lld%lld%lld",&x,&y,&q);
    num[x][y]++;
    ys[x][y][num[x][y]] = q;
  }
  printf("%lld",bfs());
  return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/179069.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

阿里“云开发“小程序(uniCould)

博主ps&#xff1a; 网上资料少的可怜&#xff0c;哎&#xff0c;腾讯云涨价了&#xff0c;论服务器&#xff0c;我肯定选的阿里&#xff0c;再着你们对比下unicould的报价就知道了&#xff0c;如果有钱就另当别论了。 所以这片博文&#xff0c;博主试过之后&#xff0c;先抛出…

(day8) 自学Java——拼图小游戏

GUI(图形用户接口)&#xff0c;是指采用图形化的方式显示操作界面。 Java中包含两套完整体系&#xff1a;AWT包&#xff0c;Swing包 一.代码 package com.abc.ui;import javax.swing.*; import javax.swing.border.BevelBorder; import java.awt.event.ActionEvent; import ja…

JavaEE day5 初识CSS 2

选择器 基本选择器&#xff1a;id选择器、类选择器、元素选择器 复合选择器&#xff1a;并列选择器、子孙选择器、孩子选择器 通配符选择器 *{.....} 任意元素&#xff1a;书写一些全局的规则时使用&#xff0c;就等于是一个全局都要遵守的规则 伪类选择器&#xff1a;针…

Lab 3: Midterm Review

Lab3 部分questionQ5&#xff1a;Its Always a Good PrimeQ6&#xff1a;Church numeralsQ5&#xff1a;It’s Always a Good Prime Implement div_by_primes_under, which takes in an integer nand returns an n-divisibility checker. An n-divisibility-checker is a funct…

【蓝桥杯选拔赛真题35】python回文数升级 青少年组蓝桥杯python 选拔赛STEMA比赛真题解析

目录 python输出N除以3的商 一、题目要求 1、编程实现 2、输入输出

二叉树22:二叉搜索树中的搜索

主要是我自己刷题的一些记录过程。如果有错可以指出哦&#xff0c;大家一起进步。 转载代码随想录 原文链接&#xff1a; 代码随想录 leetcode链接&#xff1a;700. 二叉搜索树中的搜索 题目&#xff1a; 给定二叉搜索树&#xff08;BST&#xff09;的根节点 root 和一个整数…

变量的了解

1、普通局部变量 -------------定义形式&#xff1a;在{}里面定义的 普通变量 叫做 普通局部变量 -------------作用范围&#xff1a;所在的 {} 复合语句之间有效 -------------生命周期&#xff1a;所在的 {} 复合语句之间有效 -------------存储区域&#xff1a;栈区 ---…

2.6 JAVA运算符

文章目录1.运算符概述2.运算符具体功能3.基本四则运算符4.取余运算符5.自增自减运算符6.比较运算符7.逻辑运算符8.三目运算符9.赋值运算符10.综合练习&#xff1a;求平年闰年1.运算符概述 运算符用于连接表达式的操作数&#xff0c;并对操作数执行运算。 例如&#xff0c;表达…

使用java来创建es索引(基于es7.8)

1、先引入pom依赖&#xff1a; <dependencies> <dependency> <groupId>org.elasticsearch</groupId> <artifactId>elasticsearch</artifactId> <version>7.8.0</version> …

零基础学JavaWeb开发(二十四)之 springmvc入门到精通(4)

三、整合前端layui 前后端分离架构模式 前端---页面编写好 数据源来自于 后端接口 layui或者vue等 将接口编写好即可&#xff08;springmvc&#xff09; 1、使用layui画出首页界面 html/js/css 表格 - 页面元素 - Layui 引入css <!-- 引入layui css --> <link …

MySQL优化(1)执行计划explain中type属性详解

系列文章目录1.初始化测试数据1.初始化表格&#xff08;user表&#xff09;2.初始化表格&#xff08;product表&#xff09;3.初始化表格&#xff08;user表数据初始化&#xff09;4.初始化表格&#xff08;product表函数&#xff09;5.初始化表格&#xff08;product表数据初始…

雷达编程实战之信号处理流程

信号处理_MatlabAD数据幅相校准1DFFT2DFFT由射频前端发射、接收信号&#xff0c;至获得目标径向距离、径向速度、径向角度、RCS等目标属性信息&#xff0c;这个过程&#xff0c;我们谓之&#xff0c;FMCW毫米波雷达信号处理流程。 VCO为压控振荡器(Voltage Controlled Oscilla…

从技术角度看Android大系统的构成

最近因为工作需要&#xff0c;再次捡起了放下多年的Android开发。说起Android&#xff0c;还是要感谢这个时代&#xff0c;感谢开源。这个时代&#xff0c;让我们开发者&#xff0c;可以通过开源&#xff0c;通过开源的Android&#xff0c;学到很多东西。有如此感慨&#xff0c…

基本二叉树

文章目录接口实现三种基本遍历方式获取节点个数获取叶子节点个数获取树的高度返回第K层节点个数返回x所在节点二叉树的销毁OJ二叉树知识点及选择题N0 N2 1由中序和前序&#xff08;或后序&#xff09;确定结构的方法有N个元素的完全二叉树的深度是 logN 1选择题&#xff1a;…

Linux常用命令——sftp命令

在线Linux命令查询工具(http://www.lzltool.com/LinuxCommand) sftp 交互式的文件传输程序 补充说明 sftp命令是一款交互式的文件传输程序&#xff0c;命令的运行和使用方式与ftp命令相似&#xff0c;但是&#xff0c;sftp命令对传输的所有信息使用ssh加密&#xff0c;它还…

预处理【详解】

本期介绍&#x1f356; 主要介绍&#xff1a;#define如何定义宏&#xff0c;宏替换的规则&#xff0c;为什么宏定义时不要吝啬我们的括号&#xff0c;为什么宏的参数不能带有副作用&#xff0c;宏和函数的区别。还讲解了预处理符号#和##&#xff0c;#undef指令&#xff0c;众多…

Swift(4)

目录 Dictionary集合 组合赋值符号 区间运算符 字符串字面量里的特殊字符 操作字符 Dictionary集合 相当于java中的Map集合 函数与闭包 函数也可以内嵌 函数其实就是闭包的一种特殊形式&#xff1a;一段可以被随后调用的代码块。闭包中的代码可以访问其生效范围内的变量和…

SpringMVC的@RequestMapping注解

SpringMVC的RequestMapping注解RequestMapping注解的功能RequestMapping注解的位置RequestMapping注解的value属性RequestMapping注解的method属性RequestMapping注解的params属性RequestMapping注解的headers属性什么是headers属性RequestMapping注解的功能 从注解名称上我们…

二叉树23:验证二叉搜索树

主要是我自己刷题的一些记录过程。如果有错可以指出哦&#xff0c;大家一起进步。 转载代码随想录 原文链接&#xff1a; 代码随想录 leetcode链接&#xff1a;344. 反转字符串 题目&#xff1a; 给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。…

26. 命名空间

前言&#xff1a;如果你接触过c/c/c#/java&#xff0c;那么对于python中的命名空间也是如此&#xff0c;只不过在些许地方存在细微差异&#xff0c;不过倒无伤大雅。 1. 定义 命名空间(Namespace)是从名称到对象的映射&#xff0c;大部分的命名空间都是通过 python 字典来实现…