[CTSC99]拯救大兵瑞恩

本文介绍了一种解决迷宫救援问题的方法,通过搜索算法和位运算优化,帮助士兵麦克以最短时间抵达被关押的瑞恩所在单元。详细解释了迷宫的构建方式、门和墙的定义、钥匙的获取与使用,以及如何运用BFS算法求解最短路径。同时,提供了输入输出示例和代码实现,旨在帮助读者理解并应用该算法解决类似问题。
摘要由CSDN通过智能技术生成

  一、问题描述

1944年,特种兵麦克接到国防部的命令,要求立即赶赴太平洋上的一个孤岛,营救被敌军俘虏的大兵瑞恩。瑞恩被关押在一个迷宫里,迷宫地形复杂,但是幸好麦克得到了迷宫的地形图。

迷宫的外形是一个长方形,其在南北方向被划分为N行,在东西方向被划分为M列,于是整个迷宫被划分为N*M个单元。我们用一个有序数对(单元的行号,单元的列号)来表示单元位置。南北或东西方向相邻的两个单元之间可以互通,或者存在一扇锁着的门,又或者存在一堵不可逾越的墙。迷宫中有一些单元存放着钥匙,并且所有的门被分为P类,打开同一类的门的钥匙相同,打开不同类的门的钥匙不同。

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

你的任务是帮助麦克以最快的方式抵达瑞恩所在单元,营救大兵瑞恩。

输入:

第一行是三个整数,依次表示N,M,P的值;

第二行是一个整数K,表示迷宫中门和墙的总个数;

第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)

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

输出:

输出文件只包含一个整数T,表示麦克营救到大兵瑞恩的最短时间的值,若不存在可行的营救方案则输出-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

参数设定:

3<=N,M<=15;

1<=P<=10;
-------------------------------------------------------------------------------------------------------------------
第一次看到这题时想复杂了,但后来一看数据规模,就想到了搜索,在元素上记录了附带的钥匙信息,需要用到一点位运算,这样就转化为了常规的迷宫最短路,显然BFS,注意下判重,代码附上:

const
     dx:array[1..4] of longint=(0,-1,1,0);
     dy:array[1..4] of longint=(-1,0,0,1);
var
   n,m:longint;
   g:array[1..15,1..15] of record key:longint; wall:array[1..4] of longint; end;
   v:array[1..15,1..15,0..1024] of boolean;
   top,last:longint;
   q:array[0..1024*225] of record state,x,y,dis:longint; end;
procedure swap(a,b:longint);
begin
     a:=a xor b; b:=a xor b; a:=a xor b;
end;
procedure init;
var
     p,k,xi1,yi1,xi2,yi2,gi,s,qi,i:longint;
begin
     assign(input,'rescue.in');
     reset(input);
     fillchar(g,sizeof(g),0);
     readln(n,m,p);
     readln(k);
     for i:=1 to k do begin
         readln(xi1,yi1,xi2,yi2,gi);
         if gi=0 then gi:=-1;
         if (xi1>xi2)or(yi2>yi1) then begin swap(xi1,xi2); swap(yi1,yi2); end;
         if xi1=xi2 then begin
            g[xi1,yi1].wall[4]:=gi;
            g[xi2,yi2].wall[1]:=gi;
         end else begin
             g[xi1,yi1].wall[3]:=gi;
             g[xi2,yi2].wall[2]:=gi;
         end;
     end;
     readln(s);
     for i:=1 to s do begin
         readln(xi1,yi1,qi);
         g[xi1,yi1].key:=g[xi1,yi1].key or (1 shl(qi-1));
     end;
end;
procedure upload(xi,yi,statei,disi:longint);
begin
     if v[xi,yi,statei] then exit;
     inc(last);
     with q[last] do begin
          x:=xi;
          y:=yi;
          state:=statei;
          dis:=disi;
     end;
     v[xi,yi,statei]:=true;
end;
procedure expand(top:longint);
var
   k:longint;
begin
     with q[top] do
          for k:=1 to 4 do
              if (x+dx[k]>=1)and(x+dx[k]<=n)and(y+dy[k]>=1)and(y+dy[k]<=m)and(g[x,y].wall[k]<>-1) then begin
                 if g[x,y].wall[k]=0 then
                    upload(x+dx[k],y+dy[k],state or g[x+dx[k],y+dy[k]].key,dis+1);
                 if (g[x,y].wall[k]<>0)and((state shr(g[x,y].wall[k]-1))and 1=1) then
                    upload(x+dx[k],y+dy[k],state or g[x+dx[k],y+dy[k]].key,dis+1);
              end;
end;
procedure solve;
begin
     assign(output,'rescue.out');
     rewrite(output);
     fillchar(v,sizeof(v),false);
     top:=0;
     last:=0;
     upload(1,1,g[1,1].key,0);
     while top<last do begin
           inc(top);
           with q[top] do
                if (x=n)and(y=m) then begin
                   write(dis);
                   close(output);
                   halt;
                end;
           expand(top);
     end;
     write(-1);
     close(output);
end;
begin
     init;
     solve;
end.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值