【洛谷4011】孤岛营救问题(状压SPFA)
大致题意: 有一个\(N*M\)的四联通迷宫,相邻两个可能互通,可能有一扇门,也可能有一堵墙。对于第\(i\)类的门,你需要有第\(i\)类的钥匙才可以通过。问你从\((1,1)\)到达\((N,M)\)的最短路。
第一步:建图
看到种类数\(≤10\),应该不难想到状压吧!而且,我们还可以给每个格子一个编号(计算方法:\(pos(x,y)=(x-1)*m+y\))。
我们可以用\(key_i\)来表示编号为\(i\)的格子上有的钥匙状压之后的结果,并用\(dis_{i,j}\)来表示从\((1,1)\)出发,到达编号为\(i\)的节点时拥有的钥匙种类状压之后的结果为\(j\)的最短路径。
接下来,我们可以枚举每一个节点\(i\)与其一个相邻的节点\(j\),然后分类讨论:
- 如果它们互通:在它们之间建一条边权为1的边。
- 如果它们之间有一扇门:假设这扇门的种类为\(x\),就在他们之间建一条边权为\(2^x\)的边。
- 如果它们之间有一堵墙:不建边。
其中,边权就相当于走这条边的条件,一定要当前拥有的钥匙含有开启这扇门的钥匙才行。
第二步:跑最(wang)短(luo)路(liu)
建完了图,就可以开始跑最短路了,当然,这题作为网络流24题之一,你一定要写网络流也没人拦你。
一开始初始化拥有的钥匙状压后的结果为1(不然就无法过边权为1的边),然后,每到达一个节点,就可以更新拥有的钥匙后的状压结果了。
最短路的过程应该还是比较简单的,这题难就难在建图。
代码
#include<bits/stdc++.h>
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define abs(x) ((x)<0?-(x):(x))
#define LL long long
#define ull unsigned long long
#define swap(x,y) (x^=y,y^=x,x^=y)
#define tc() (A==B&&(B=(A=ff)+fread(ff,1,100000,stdin),A==B)?EOF:*A++)
#define pc(ch) (pp_<100000?pp[pp_++]=(ch):(fwrite(pp,1,100000,stdout),pp[(pp_=0)++]=(ch)))
#define N 10
#define pos(x,y) ((x-1)*m+y)
#define add(x,y,z) (e[++ee].to=y,e[ee].nxt=lnk[x],e[lnk[x]=ee].val=z)
int pp_=0;char ff[100000],*A=ff,*B=ff,pp[100000];
using namespace std;
int n,m,s,ans=1e9,ee=0,Inqueue[N*N+5][1<<N+1],dis[N*N+5][1<<N+1],lnk[N*N+5],keys[N*N+5],f[N*N+5][N*N+5];
struct edge
{
int to,nxt,val;
}e[((N*N)<<2)+5];
struct Status
{
int x,v;
}q[((N*N)<<N+1)+5];
inline void read(int &x)
{
x=0;int f=1;static char ch;
while(!isdigit(ch=tc())) f=ch^'-'?1:-1;
while(x=(x<<3)+(x<<1)+ch-48,isdigit(ch=tc()));
x*=f;
}
inline void write(int x)
{
if(x<0) pc('-'),x=-x;
if(x>9) write(x/10);
pc(x%10+'0');
}
inline void SPFA()//跑最短路
{
register int i,j,H=Inqueue[1][1]=1,T=1,maxx=1<<s+1,MOD=(N*N)<<N+1;register Status k;q[1]=(Status){1,1};
for(i=1;i<=n*m;++i) for(j=0;j<maxx;++j) dis[i][j]=1e9;dis[1][1]=0;
while((H%=MOD)^(T+1))//只要队列没有空
{
k=q[H++],Inqueue[k.x][k.v]=0,dis[k.x][k.v|keys[k.x]]=min(dis[k.x][k.v|keys[k.x]],dis[k.x][k.v]),k.v|=keys[k.x];//更新当前拥有的钥匙的数量
for(i=lnk[k.x];i;i=e[i].nxt)//枚举当前节点能到达的每一个节点
{
if((k.v&e[i].val)^e[i].val) continue;//如果不满足条件,就跳过
if(dis[k.x][k.v]+1<dis[e[i].to][k.v])//如果更短就更新最短距离
{
dis[e[i].to][k.v]=dis[k.x][k.v]+1;
if(!Inqueue[e[i].to][k.v]) Inqueue[e[i].to][k.v]=1,q[(++T)%=MOD]=(Status){e[i].to,k.v};//如果不在队列中,就将其加入队列
}
}
}
for(i=0;i<maxx;++i) ans=min(ans,dis[n*m][i]);//求出最优的答案
}
int main()
{
register int i,j,t,x,y,z;
for(read(n),read(m),read(s),i=1;i<=n*m;++i) for(j=1;j<=n*m;++j) f[i][j]=-1;
for(read(t);t;--t)
{
static int u,v;
read(x),read(y),u=pos(x,y),read(x),read(y),v=pos(x,y),read(x),f[u][v]=f[v][u]=x;
if(x) add(u,v,1<<f[u][v]),add(v,u,1<<f[u][v]);//如果这不是一堵墙,就新建一条边
}
for(i=1;i<=n;++i)
{
for(j=1;j<=m;++j)
{
//对于互通的情况建边
if(i>1&&!(~f[pos(i,j)][pos(i-1,j)])) add(pos(i,j),pos(i-1,j),1);
if(i<n&&!(~f[pos(i,j)][pos(i+1,j)])) add(pos(i,j),pos(i+1,j),1);
if(j>1&&!(~f[pos(i,j)][pos(i,j-1)])) add(pos(i,j),pos(i,j-1),1);
if(j<m&&!(~f[pos(i,j)][pos(i,j+1)])) add(pos(i,j),pos(i,j+1),1);
}
}
for(read(t);t;--t) read(x),read(y),read(z),keys[pos(x,y)]|=1<<z;//读入每一把钥匙,更新每一个位置的钥匙种类状压后的值
return SPFA(),write(ans^1000000000?ans:-1),fwrite(pp,1,pp_,stdout),0;
}
【洛谷4011】孤岛营救问题(状压SPFA)的更多相关文章
- 洛谷P4011 孤岛营救问题(状压+BFS)
传送门 和网络流有半毛钱关系么…… 可以发现$n,m,p$都特别小,那么考虑状压,每一个状态表示位置以及钥匙的拥有情况,然后每次因为只能走一步,所以可以用bfs求出最优解 然后是某大佬说的注意点:每个 ...
- 【题解】洛谷P3959 [NOIP2017TG] 宝藏(状压DP+DFS)
洛谷P3959:https://www.luogu.org/problemnew/show/P3959 前言 NOIP2017时还很弱(现在也很弱 看出来是DP 但是并不会状压DP 现在看来思路并不复 ...
- 洛谷 [P4011] 孤岛营救问题
状压+BFS 通过观察数据范围可知,我们应该状压钥匙种类,直接BFS即可 注意,一个点处可能不知有一把钥匙 #include <iostream> #include <cstdio& ...
- 洛谷 P2473 [SCOI2008]奖励关(状压dp+期望)
题面 luogu 题解 \(n \leq 15\) 状压 \(f[i][S]\)表示第\(i\)轮,吃过的集合为\(S\) 正着转移好像有点复杂 考虑逆推转移(正着转移应该也行) \(f[i][S]\ ...
- 洛谷 P1278 单词游戏 【状压dp】
题目描述 Io和Ao在玩一个单词游戏. 他们轮流说出一个仅包含元音字母的单词,并且后一个单词的第一个字母必须与前一个单词的最后一个字母一致. 游戏可以从任何一个单词开始. 任何单词禁止说两遍,游戏中只 ...
- 洛谷P2761 软件补丁问题 [状压DP,SPFA]
题目传送门 软件补丁问题 题目描述 T 公司发现其研制的一个软件中有 n 个错误,随即为该软件发放了一批共 m 个补丁程序.每一个补丁程序都有其特定的适用环境,某个补丁只有在软件中包含某些错误而同时又 ...
- 洛谷P2704 [NOI2001]炮兵阵地 [状压DP]
题目传送门 炮兵阵地 题目描述 司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队.一个N*M的地图由N行M列组成,地图的每一格可能是山地(用“H” 表示),也可能是平原(用“P”表示),如下图 ...
- 洛谷P2761 软件补丁问题(状压DP,SPFA)
题意 描述不清... Sol 网络流24题里面怎么会有状压dp?? 真是狗血,不过还是简单吧. 直接用$f[sta]$表示当前状态为$sta$时的最小花费 转移的时候枚举一下哪一个补丁可以搞这个状态 ...
- 洛谷 P3622 [APIO2007]动物园【状压dp】
看成网络流建图想了好久... 实际上5个是可以状压的 设f[i][k]为到第i个围栏状态为k的方案数,因为考虑到重复,设g[i][k]记录i开始,状态为k的孩子有几个 状态转移很好想:f[j][k]= ...
- 洛谷 P4011 孤岛营救问题【bfs】
注意: 一个点可能有多把钥匙,所以把每个点有钥匙的情况状压一下 两个点之间有障碍的情况只给出了单向,存的时候记得存一下反向 b[i][j]表示当前点拥有钥匙的状态,g[x1][y1][x2][y2]表 ...
随机推荐
- Eclipse 整合SpringMybatis,SpringMVC,用Maven管理项目搭建详情
环境:JDK下载地址 https://pan.baidu.com/s/1UyvEAI-4Ci6TDdVJiYUUiQ 密码:ma51 IDE:eclipse下载地址 https://pan.baidu ...
- shell脚本——循环和函数
1.打印一个等腰三角形 ` ;do -$i]` ;do echo -n ' ' done -]` ;do echo -n '*' done echo done 2.打印99乘法表 #!/bin/bas ...
- return this链式操作
function Fn(){}; Fn.prototype = { constructor:Fn, a:function(){ alert(1); return this; //实现链式操作.即fn. ...
- 7、python数据类型之集合set
数据类型之集合setset 不允许重复的无序集合,不能通过下标取值,因为无序1.创建 创建空集合 s ={} 默认类型为字典,所以不是空集合,空集合如下 s = set() s = { ...
- crawlspider的源码学习
Spider基本上能做很多事情了,但是如果你想爬取全站的话,可能需要一个更强大的武器.CrawlSpider基于Spider,但是可以说是为全站爬取而生.CrawlSpiders是Spider的派生类 ...
- Chapter10
package scala import java.io.{PrintStream, PrintWriter}import java.util.Date import scala.util.loggi ...
- mysql CPU占用高
https://blog.csdn.net/u011239989/article/details/72863333 QPS高,sql简单的场景下, 需要 1. 提高数据库的服务器性能CPU 内存等 2 ...
- php配置redis
redis扩展程序下载链接(版本3.0.504) 链接: https://pan.baidu.com/s/1QfrjtnifOl8SPf7XCEly2g 密码: u6x6 wampserver下载地址 ...
- HDU 5775 L - Bubble Sort 树状数组
给定一段冒泡排序的代码,要求输出每个数字能到达的最右边的位置和最左边的位置的差 因为那段冒泡排序的代码是每次选取一个最小的数,放在左边的,所以,每个数最多能到达右边的位置应该是起始位置i+右边有多少个 ...
- mysql双主互备
mysql主从同步使得数据可以从一个数据库服务器复制到其他服务器上,在复制数据时,一个服务器充当主服务器(master),其余的服务器充当从服务器(slave),备服务器从主服务器同步数据,完成数据的 ...