点此看题面

大致题意: 有一个\(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)的更多相关文章

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

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

  2. 【题解】洛谷P3959 [NOIP2017TG] 宝藏(状压DP+DFS)

    洛谷P3959:https://www.luogu.org/problemnew/show/P3959 前言 NOIP2017时还很弱(现在也很弱 看出来是DP 但是并不会状压DP 现在看来思路并不复 ...

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

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

  4. 洛谷 P2473 [SCOI2008]奖励关(状压dp+期望)

    题面 luogu 题解 \(n \leq 15\) 状压 \(f[i][S]\)表示第\(i\)轮,吃过的集合为\(S\) 正着转移好像有点复杂 考虑逆推转移(正着转移应该也行) \(f[i][S]\ ...

  5. 洛谷 P1278 单词游戏 【状压dp】

    题目描述 Io和Ao在玩一个单词游戏. 他们轮流说出一个仅包含元音字母的单词,并且后一个单词的第一个字母必须与前一个单词的最后一个字母一致. 游戏可以从任何一个单词开始. 任何单词禁止说两遍,游戏中只 ...

  6. 洛谷P2761 软件补丁问题 [状压DP,SPFA]

    题目传送门 软件补丁问题 题目描述 T 公司发现其研制的一个软件中有 n 个错误,随即为该软件发放了一批共 m 个补丁程序.每一个补丁程序都有其特定的适用环境,某个补丁只有在软件中包含某些错误而同时又 ...

  7. 洛谷P2704 [NOI2001]炮兵阵地 [状压DP]

    题目传送门 炮兵阵地 题目描述 司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队.一个N*M的地图由N行M列组成,地图的每一格可能是山地(用“H” 表示),也可能是平原(用“P”表示),如下图 ...

  8. 洛谷P2761 软件补丁问题(状压DP,SPFA)

    题意 描述不清... Sol 网络流24题里面怎么会有状压dp?? 真是狗血,不过还是简单吧. 直接用$f[sta]$表示当前状态为$sta$时的最小花费 转移的时候枚举一下哪一个补丁可以搞这个状态 ...

  9. 洛谷 P3622 [APIO2007]动物园【状压dp】

    看成网络流建图想了好久... 实际上5个是可以状压的 设f[i][k]为到第i个围栏状态为k的方案数,因为考虑到重复,设g[i][k]记录i开始,状态为k的孩子有几个 状态转移很好想:f[j][k]= ...

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

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

随机推荐

  1. Codeforces Beta Round #71 C【KMP+DP】

    Codeforces79C 题意: 求s串的最大子串不包含任意b串: 思路: dp[i]为以i为起点的子串的最长延长距离. 我们可以想到一种情况就是这个 i 是某个子串的起点,子串的长度-1就是最短, ...

  2. 讲完.class,Class之后,继续。

    讲完.class,Class之后,继续. 1)泛化的Class引用 Class也可以加入泛型,加入之后会进行类型检查. 贴一下书上原话,Class<?>优于Class,虽然他们是等价的,C ...

  3. promise封装小程序的请求类(request,清爽易懂)

    话不多说直接上代码,清爽易懂: import { config } from '../config.js' const tips = { 1:'抱歉出现了一个错误', 2:'网络错误', 1005:' ...

  4. Leetcode题解

    前言 Leetcode现在弄了一个Weekly Contest,然后题目又会作为新题目:感觉如果现在还不及时刷题的话可能真的赶不上它题目增长的速度了.......题目会在博客和Github上同步更新的 ...

  5. DropDownList 不能绑定多个值错误!

    ListItem item = new ListItem("--请选择--",""); ddlstPurchaser.Items.Insert(0, item) ...

  6. docker镜像的分层结构三

    docker的镜像分层 docker里的镜像绝大部分都是在别的镜像的基础上去进行创建的,也就是使用镜像的分层结构. 实验 比如说使用dockerfile去创建一个最简单的hello镜像.创建好对应的d ...

  7. 移动端APP第一次登录和自动登录流程

    App登陆保存数据流程App因为要实现自动登陆功能,所以必然要保存一些凭据,所以比较复杂. App登陆要实现的功能: 密码不会明文存储,并且不能反编绎解密: 在服务器端可以控制App端的登陆有效性,防 ...

  8. TOMCAT热部署 catalina.home catalina.base

    catalina.home 一台机器通常只有一个, 指向Tomcat的安装目录 catalina.base  一台机器可以启动多个Context, 每个Context对应一个catalina.base ...

  9. 积分图像的应用(二):非局部均值去噪(NL-means)

    非局部均值去噪(NL-means)一文介绍了NL-means基本算法,同时指出了该算法效率低的问题,本文将使用积分图像技术对该算法进行加速. 假设图像共像个素点,搜索窗口大小,领域窗口大小, 计算两个 ...

  10. linux 向文本指定位置写入内容

    sed -i "37 r a.txt" test.txt ====== 向test.txt 的第37行后,也就是38行后写入a.txt的内容 sed -i "38i aa ...