P4011 孤岛营救问题

题意

题目描述

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

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

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

输入输出格式

输入格式:

第\(1\)行有\(3\)个整数,分别表示\(N,M,P\)的值。

第\(2\)行是\(1\)个整数\(K\),表示迷宫中门和墙的总数。

第\(I+2\)行\((1 \leq I \leq K)\),有\(5\)个整数,依次为\(X_{i1},Y_{i1},X_{i2},Y_{i2},G_i\):

  • 当\(G_i \geq 1\)时,表示\((X_{i1},Y_{i1})\)单元与\((X_{i2},Y_{i2})\)单元之间有一扇第\(G_i\)类的门
  • 当\(G_i=0\)时,表示\((X_{i1},Y_{i1})\)单元与\((X_{i2},Y_{i2})\)单元之间有一堵不可逾越的墙(其中,\(|X_{i1}-X_{i2}|+|Y_{i1}-Y_{i2}|=1\),\(0 \leq G_i \leq P\))。

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

第\(K+3+J\)行\((1 \leq J \leq S)\),有\(3\)个整数,依次为\(X_{i1},Y_{i1},Q_i\):表示第\(J\)把钥匙存放在\((X_{i1},Y_{i1})\)单元里,并且第\(J\)把钥匙是用来开启第\(Q_i\)类门的。(其中\(1 \leq Q_i \leq P\))。

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

输出格式:

将麦克营救到大兵瑞恩的最短时间的值输出。如果问题无解,则输出\(-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

输出样例:

14

说明

\(|X_{i1}-X_{i2}|+|Y_{i1}-Y_{i2}|=1,0 \leq G_i \leq P\)

\(1 \leq Q_i \leq P\)

\(N,M,P \leq 10, K<150,S \leq 14\)

思路

这题我调了一晚上,本来想第二天给你们装逼的,可惜没调出来。 --digger_sun

这是一道很不错的题,可以用多种方式\(AC\):\(A^*\)、最短路、网络流...在这里我选择的是状态压缩+最短路。

定义\(dis[i][j]\)表示在\(i\)号结点时拥有的钥匙状态为\(j\)的最短路径长度。其中,\(j\)的二进制表示下第\(k\)位表示是否拥有钥匙\(k\)。建图时,互相能连通的两点之间距离都为一,走到有钥匙的点时一定拿钥匙,稳妥地处理输入信息,就能\(AC\)了,码量也不大。

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int INF=0x3f3f3f3f;
int n,m,p,k,ans=INF,dis[105][1<<15],G[105][105],gate[105][105],key[105];
int cnt,top[105],to[1005],len[1005],ned[1005],nex[1005];
bool vis[105][1<<15];
int a[2]={+1,+0};
int b[2]={+0,+1};
int read()
{
int re=0;
char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) re=(re<<3)+(re<<1)+ch-'0',ch=getchar();
return re;
}
inline int f(int x,int y){return (x-1)*m+y;}
inline void add_edge(int x,int y,int z,int w)
{
to[++cnt]=y,len[cnt]=z,ned[cnt]=w,nex[cnt]=top[x],top[x]=cnt;
to[++cnt]=x,len[cnt]=z,ned[cnt]=w,nex[cnt]=top[y],top[y]=cnt;
}
void SPFA()
{
memset(dis,0x3f,sizeof dis);
queue<PII>Q;
dis[1][key[1]]=0;
Q.push(make_pair(1,key[1]));
while(!Q.empty())
{
int now=Q.front().first,con=Q.front().second;Q.pop();
vis[now][con]=false;
for(int i=top[now];i;i=nex[i])
if((ned[i]&con)==ned[i])
if(dis[to[i]][con|key[to[i]]]>dis[now][con]+len[i])
{
dis[to[i]][con|key[to[i]]]=dis[now][con]+len[i];
if(!vis[to[i]][con|key[to[i]]])
{
vis[to[i]][con|key[to[i]]]=true;
Q.push(make_pair(to[i],con|key[to[i]]));
}
}
}
}
int main()
{
n=read(),m=read(),p=read(),k=read();
while(k--)
{
int x=read(),y=read(),xx=read(),yy=read(),g=read();
if(!g) G[f(x,y)][f(xx,yy)]=G[f(xx,yy)][f(x,y)]=INF;
else gate[f(x,y)][f(xx,yy)]=gate[f(xx,yy)][f(x,y)]=(1<<(g-1));
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int w=0;w<2;w++)
{
int x=i+a[w],y=j+b[w];
if(x<=0||x>n||y<=0||y>m) continue;
add_edge(f(i,j),f(x,y),G[f(i,j)][f(x,y)]==INF?INF:1,gate[f(i,j)][f(x,y)]);
}
k=read();
while(k--)
{
int x=read(),y=read(),z=read();
key[f(x,y)]|=(1<<(z-1));
}
SPFA();
for(int i=0;i<(1<<p);i++) ans=min(ans,dis[f(n,m)][i]);
printf("%d",ans==INF?-1:ans);
return 0;
}

Luogu P4011 孤岛营救问题(状态压缩+最短路)的更多相关文章

  1. Luogu P4011 孤岛营救问题

    题目链接 \(Click\) \(Here\) 注意坑点:一个地方可以有多把钥匙. 被卡了一会,调出来发现忘了取出来实际的数字,直接把二进制位或上去了\(TwT\),其他的就是套路的分层图最短路.不算 ...

  2. 洛谷 P4011 孤岛营救问题【最短路+分层图】

    题外话:昨夜脑子昏沉,今早一调试就过了...错误有:我忘记还有墙直接穿墙过...memset初始化INF用错了数...然后手残敲错一个状态一直过不了样例...要是这状态去比赛我简直完了......or ...

  3. luogu P2704 炮兵阵地(经典状态压缩DP)

    方格有m*n个格子,一共有2^(m+n)种排列,很显然不能使用暴力法,因而选用动态规划求解. 求解DP问题一般有3步,即定义出一个状态 求出状态转移方程 再用算法实现.多数DP题难youguan点在于 ...

  4. P4011 孤岛营救问题

    \(\color{#0066ff}{题目描述}\) 1944 年,特种兵麦克接到国防部的命令,要求立即赶赴太平洋上的一个孤岛,营救被敌军俘虏的大兵瑞恩.瑞恩被关押在一个迷宫里,迷宫地形复杂,但幸好麦克 ...

  5. 洛谷P4011 孤岛营救问题(状压+BFS)

    传送门 和网络流有半毛钱关系么…… 可以发现$n,m,p$都特别小,那么考虑状压,每一个状态表示位置以及钥匙的拥有情况,然后每次因为只能走一步,所以可以用bfs求出最优解 然后是某大佬说的注意点:每个 ...

  6. 【Luogu】P2258子矩阵(状态压缩,DP)

    233今天蒟蒻我连文化课都没听光想着这个了 然后我调了一下午终于过了!!! 一看数据范围似乎是状压,然而216等于65536.开一个65536*65536的二维数组似乎不太现实. 所以Rqy在四月还是 ...

  7. 洛谷 P4011 孤岛营救问题【bfs】

    注意: 一个点可能有多把钥匙,所以把每个点有钥匙的情况状压一下 两个点之间有障碍的情况只给出了单向,存的时候记得存一下反向 b[i][j]表示当前点拥有钥匙的状态,g[x1][y1][x2][y2]表 ...

  8. 洛谷 [P4011] 孤岛营救问题

    状压+BFS 通过观察数据范围可知,我们应该状压钥匙种类,直接BFS即可 注意,一个点处可能不知有一把钥匙 #include <iostream> #include <cstdio& ...

  9. P2622 关灯问题II (状态压缩,最短路)

    题目链接 Solution 这道题算是很经典的状压问题了,好题. 考虑到 \(n\) 的范围仅为 \(10\) , 那么也就是说所有状态压起来也只有 \(1024\) 种情况. 然后我们发现 \(m\ ...

随机推荐

  1. 初识OpenCV-Python - 007: 平滑图像

    本节内容主要将如何平滑图像.如通过低通道滤波模糊图像.或者自定义滤波处理图像. import cv2import numpy as npfrom matplotlib import pyplot as ...

  2. USACO training course Mother's Milk /// DFS(有点意思) oj10120

    题目大意: 输入 A B C 为三个容器的容量 一开始A B是空的 C是满的 每一次倾倒只能在 盛的容器满 或 倒的容器空 时才停止 输出当A容器空时 C容器内剩余量的所有可能值 Sample Inp ...

  3. USACO 2013 January Silver Painting the Fence /// oj23695

    题目大意: 输入n,k :n次操作 找到覆盖次数在k及以上的段的总长 一开始位置在0 左右活动范围为1-1000000000 接下来n行描述每次操作的步数和方向 Sample Input 6 22 R ...

  4. mysql commond record

    CREATE DATABASE IF NOT EXISTS codex_gm DEFAULT CHARACTER SET utf8; service mysqld stop screen -dmS m ...

  5. HDU3480 Division——四边形不等式或斜率优化

    题目大意 将N个数分成M部分,使每部分的最大值与最小值平方差的和最小. 思路 首先肯定要将数列排序,每部分一定是取连续的一段,于是就有了方程 $\Large f(i,j)=min(f(i-1,k-1) ...

  6. Ionic3 demo TallyBook 实例2

    1.添加插件 2.相关页面 消费页面: <ion-header> <ion-navbar> <ion-title> 消费记录 </ion-title> ...

  7. JavaScript学习笔记:数组的indexOf()和lastindexOf()方法

    https://www.w3cplus.com/javascript/array-part-6.html

  8. 密码学笔记(5)——Rabin密码体制和语义安全性

    一.Rabin密码体制 Rabin密码体制是RSA密码体制的一种,假定模数$n=pq$不能被分解,该类体制对于选择明文攻击是计算安全的.因此,Rabin密码体制提供了一个可证明安全的密码体制的例子:假 ...

  9. windows环境下运行Elasticsearch

    1.Elasticsearch下载地址:https://github.com/medcl/elasticsearch-rtf 直接下载ZIP包: 2.配置JAVA环境 jdk64位地址:jdk-win ...

  10. HttpServletRequest request 获取当前登录的用户-获取当前用户

    有的业务需要知道当前登录的用户 当然需要引用这个啦 import javax.servlet.http.HttpServletRequest; 然后 HttpSession session = req ...