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. webjars和springboot热启动

    webjars WebJars将Web前端Javascript和CSS等资源打包成Java的Jar包, 以便能使Maven的依赖管理支持静态JavaScript库/CSS库,比如jQuery.layu ...

  2. 初识OpenCV-Python - 009: 图像梯度

    本节学习找到图像的梯度和边界.只要用到的函数为: cv2.Sobel(), cv2.Scharr(), cv2.Laplacian() 1. Laplacian 和 Sobel的对比 import c ...

  3. VMware Workstation 10 简体中文安装教程

    分享到 一键分享 QQ空间 新浪微博 百度云收藏 人人网 腾讯微博 百度相册 开心网 腾讯朋友 百度贴吧 豆瓣网 搜狐微博 百度新首页 QQ好友 和讯微博 更多... 百度分享 分享到 一键分享 QQ ...

  4. netty 私有协议栈

    通信协议从广义上区分,可以分为公有协议和私有协议.由于私有协议的灵活性,它往往会在某个公司或者组织内部使用,按需定制,也因为如此,升级起来会非常方便,灵活性好.绝大多数的私有协议传输层都基于TCP/I ...

  5. MYSQL随笔心得1

    cmd链接数据库命令: 输入密码进入 显示全部的数据库: 退出服务器连接,还有/p   quit 非关系型数据库:NOSQL,not only sql 不仅仅是SQL 代表:redis,mongodb

  6. 编译安装redis-3.2.9(latest stable version)

    What is the Redis? Redis is an open source (BSD licensed), in-memory data structure store, used as a ...

  7. Apache Flink 1.9.0版本新功能介绍

    摘要:Apache Flink是一个面向分布式数据流处理和批量数据处理的开源计算平台,它能够基于同一个Flink运行时,提供支持流处理和批处理两种类型应用的功能.目前,Apache Flink 1.9 ...

  8. Cordova 常用命令及插件

    安装 cordova: npm install -g cordova 创建应用程序 cordova create hello com.example.hello HelloWorld  添加平台 co ...

  9. hdu 4563

    hdu 4563 把每个命令走的距离抽象成完全背包 枚举最后一个不是整点走完的命令 #include <iostream> #include <algorithm> #incl ...

  10. Windows Server 2008 R2 部署服务

    Windows Server 2008 R2 部署服务 部分参考: Windows Server 2008 R2 部署服务 - 马睿的技术博客 - 51CTO技术博客http://marui.blog ...