CTSC1999 rescue

原题:

http://codewaysky.sinaapp.com/problem.php?id=1055

  

题目描述

1944年,特种兵麦克接到国防部的命令,要求立即赶赴太平洋上的一个孤岛,营救被敌军俘虏的大兵瑞恩。瑞恩被关押在一个迷宫里,迷宫地形复杂,但是幸好麦克得到了迷宫的地形图。
迷宫的外形是一个长方形,其在南北方向被划分为N行,在东西方向被划分为M列,于是整个迷宫被划分为N×M个单元。我们用一个有序数对(单元的行号,单元的列号)来表示单元位置。南北或东西方向相邻的两个单元之间可以互通,或者存在一扇锁着的门,又或者存在一堵不可逾越的墙。迷宫中有一些单元存放着钥匙,并且所有的门被分为P类,打开同一类的门的钥匙相同,打开不同类的门的钥匙不同。
大兵瑞恩被关押在迷宫的东南角,即(N,M)单元里,并已经昏迷。迷宫只有一个入口,在西北角,也就是说,麦克可以直接进入(1,1)单元。另外,麦克从一个单元移动到另一个相邻单元的时间为1,拿取所在单元的钥匙的时间以及用钥匙开门的时间忽略不计。

输入

第一行是三个整数,依次表示N,M,P的值;(3≤N,M≤15,1≤P≤10)
第二行是一个整数K,表示迷宫中门和墙的总个数;
i+2行(1≤iK),有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≤GiP)
K+3 行是一个整数S,表示迷宫中存放的钥匙总数;
K+3+j行(1≤jS),有3个整数,依次为Xi1,Yi1,Qi:表示第j把钥匙存放在(Xi1,Yi1) 单元里,并且第j把钥匙是用来开启第Qi类门的。(其中1≤QiP)
注意:输入数据中同一行各相邻整数之间用一个空格分隔。

输出

对每组数据只输出一个整数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

提示

本题共6个测试点

来源

CTSC1999

    

    

  方法:见 : “分层图思想”及其在信息学竞赛中的应用 (IOI2004国家集训队论文)

 

  另:

  [转] http://blog.sina.com.cn/s/blog_7ff7f6960100shiz.html

  

算法:分层图最短路

在不考虑有门和钥匙的时候,就是简单的最短路,把相邻且没有墙的格子两两相连,权值为一,然后求最短路就可以。

加入门的限制后,无法仅仅用最短路解决,所以将原图(用单独的数组存储格子之间的关系)复制2^p份,num[I,j,i1,i2,i3,i4,i5,i6,i7,i8,i9,i10]记录在拥有不同钥匙的情况下的节点编号,I记录行,j记录列,i1~i10取值范围为[0..1],0表示没有该种钥匙,1表示有(多少把都可以),将有关系的节点之间连边,再整体做一遍SPFA就可以得到最优解

有关系的节点:

1)相邻节点:中间没有墙和门或者有门但是两节点都在有该门钥匙的图上,则两节点间连一条权值为1的无向边

2)对应同一位置,且该位置有钥匙,且一个节点位于无该钥匙的图上,另一节点位于有该钥匙的图上,则从无钥匙的节点向有钥匙的节点连一条权值为0的有向边

注:最优情况下不一定有全部的钥匙,即i1~i10不一定都取1

总结:这道题用了快一天的时间,一上午都没有写出来的主要原因还是不敢用多重循环(十层),对节点记录的方法也没有想到,下午在做了一道同样是分层图最短 路的题后,有了记录节点编号的方法,构图也就相对容易了,加上十层循环后,基本上没有用太多的时间调试就AC了,遇到比较复杂,用最短路无法直接解决的问 题,应该考虑到分层,将一个复杂的问题转化为多个简单的问题来解决

 

代码:

const

dx: array[1..4] of -1..1=(0,1,0,-1);

dy: array[1..4] of -1..1=(1,0,-1,0);

type

point=^node;

node=record

next: point;

y, d: longint;

end;

re=record

kx, ky, can: longint;

ss: longint;

end;

var

i, j, k, n, m, ans, p: longint;

ks, sum, tot: longint;

num: array[0..15,0..15,0..1,0..1,0..1,0..1,0..1,0..1,0..1,0..1,0..1,0..1] of longint;

a: array[0..240000] of node; //邻接表存储图

dis: array[0..400000] of longint;

b: array[0..400000] of boolean;

h: array[0..4000000] of longint;

key: array[0..11] of re; //记录钥匙的相关信息

g: array[0..15,0..15,0..15,0..15] of longint; //存储最原始的图,即两节点之间是否有障碍,有的话具体是什么

x1, x2, y1, y2, q: longint;

num1, num2: longint;

each: array[0..16,0..16,0..11] of longint; //因为一个格子内可能有多把钥匙,所以需要记录数量

link: array[0..16,0..16,0..11] of longint; //记录在[I,j]格子内,第k把钥匙是什么

i1, i2, i3, i4, i5, i6, i7, i8, i9, i10: longint;

na: longint;

 

procedure ins(i,j,w: longint);

var

pp: point;

begin

new(pp);

pp^.y := j;

pp^.d := w;

pp^.next := a[i].next;

a[i].next := pp;

end;

 

procedure buildgragh; //构图

var

i, j, k: longint;

ii: longint;

xx, yy, tmp: longint;

z: array[1..10] of longint;

//i1, i2, i3, i4, i5, i6, i7, i8, i9, i10: longint;

begin

for i := 1 to n do

for j := 1 to m do

for z[1] := 0 to key[1].ss do

for z[2] := 0 to key[2].ss do

for z[3] := 0 to key[3].ss do

for z[4] := 0 to key[4].ss do

for z[5] := 0 to key[5].ss do

for z[6] := 0 to key[6].ss do

for z[7] := 0 to key[7].ss do

for z[8] := 0 to key[8].ss do

for z[9] := 0 to key[9].ss do

for z[10] := 0 to key[10].ss do begin //十重循环来枚举当前所拥有钥匙的状态

for ii := 1 to 4 do begin

xx := i+dx[ii];

yy := j+dy[ii];

if (xx>0) and (xx<=n) and (yy>0) and (yy<=m) and (g[i,j,xx,yy]<>-1) then begin

if g[i,j,xx,yy]=0 then begin //没有障碍

ins(num[i,j,z[1],z[2],z[3],z[4],z[5],z[6],z[7],z[8],z[9],z[10]],num[xx,yy,z[1],z[2],z[3],z[4],z[5],z[6],z[7],z[8],z[9],z[10]],1);

ins(num[xx,yy,z[1],z[2],z[3],z[4],z[5],z[6],z[7],z[8],z[9],z[10]],num[i,j,z[1],z[2],z[3],z[4],z[5],z[6],z[7],z[8],z[9],z[10]],1);

end else begin   //有障碍但是能够打开

tmp := g[i,j,xx,yy];

if z[tmp]=1 then begin

ins(num[i,j,z[1],z[2],z[3],z[4],z[5],z[6],z[7],z[8],z[9],z[10]],num[xx,yy,z[1],z[2],z[3],z[4],z[5],z[6],z[7],z[8],z[9],z[10]],1); 

ins(num[xx,yy,z[1],z[2],z[3],z[4],z[5],z[6],z[7],z[8],z[9],z[10]],num[i,j,z[1],z[2],z[3],z[4],z[5],z[6],z[7],z[8],z[9],z[10]],1);

end;

end;

end;

end;

if (each[i,j,0]>0) then begin  //当前格子有钥匙

for k := 1 to each[i,j,0] do if (z[link[i,j,k]]=1) then begin //当前节点在有这把钥匙的图上

tmp := link[i,j,k];

xx := num[i,j,z[1],z[2],z[3],z[4],z[5],z[6],z[7],z[8],z[9],z[10]];

case tmp of

1:yy := num[i,j,0,z[2],z[3],z[4],z[5],z[6],z[7],z[8],z[9],z[10]];

2:yy := num[i,j,z[1],0,z[3],z[4],z[5],z[6],z[7],z[8],z[9],z[10]];

3:yy := num[i,j,z[1],z[2],0,z[4],z[5],z[6],z[7],z[8],z[9],z[10]];

4:yy := num[i,j,z[1],z[2],z[3],0,z[5],z[6],z[7],z[8],z[9],z[10]];

5:yy := num[i,j,z[1],z[2],z[3],z[4],0,z[6],z[7],z[8],z[9],z[10]];

6:yy := num[i,j,z[1],z[2],z[3],z[4],z[5],0,z[7],z[8],z[9],z[10]];

7:yy := num[i,j,z[1],z[2],z[3],z[4],z[5],z[6],0,z[8],z[9],z[10]];

8:yy := num[i,j,z[1],z[2],z[3],z[4],z[5],z[6],z[7],0,z[9],z[10]];

9:yy := num[i,j,z[1],z[2],z[3],z[4],z[5],z[6],z[7],z[8],0,z[10]];

10:yy := num[i,j,z[1],z[2],z[3],z[4],z[5],z[6],z[7],z[8],z[9],0];

end;

ins(yy,xx,0); //与代表同一格子,但是在无该钥匙的图上的节点相连

end;

 

end;

 

end;

end;

 

procedure spfa(st: longint);

var

i, j, k, head, tail, now: longint;

pp: point;

begin

head := 1;

tail := 1;

fillchar(b,sizeof(b),false);

b[st] := true;

fillchar(dis,sizeof(dis),100);

na := dis[3];

dis[st] := 0;

h[1] := st;

while head<=tail do begin

now := h[head];

new(pp);

pp := a[now].next;

while pp<>nil do begin

j := pp^.y;

if dis[now]+pp^.d<dis[j] then begin

dis[j] := dis[now]+pp^.d;

if not b[j] then begin

b[j] := true;

inc(tail);

h[tail] := j;

end;

end;

pp := pp^.next;

end;

b[now] := false;

inc(head);

end;

end;

 

begin

assign(input,'d:\01.in');

reset(input);

assign(output,'d:\01.out');

rewrite(output);

readln(n,m,p);

readln(sum);

fillchar(g,sizeof(g),0);

for i := 1 to sum do begin

readln(x1,y1,x2,y2,q);

if q<>0 then g[x1,y1,x2,y2] := q else g[x1,y1,x2,y2] := -1;

g[x2,y2,x1,y1] := g[x1,y1,x2,y2];

end;

readln(ks);

fillchar(key,sizeof(key),0);

fillchar(link,sizeof(link),0);

fillchar(each,sizeof(each),0);

for i := 1 to ks do begin

readln(x1,y1,q);

key[q].kx := x1;

key[q].ky := y1;

//inc(key[q].ss);

key[q].ss := 1;  //不用管具体有几把,只要有这种钥匙,就赋为1(用于循环)

inc(each[x1,y1,0]);

link[x1,y1,each[x1,y1,0]] := q; //记录这个格子的钥匙情况

end;

tot := 0;

for i := 1 to n do

for j := 1 to m do if ks>=1 then

for i1 := 0 to key[1].ss do //if ks>=2 then

for i2 := 0 to key[2].ss do //if ks>=3 then

for i3 := 0 to key[3].ss do //if ks>=4 then

for i4 := 0 to key[4].ss do //if ks>=5 then

for i5 := 0 to key[5].ss do //if ks>=6 then

for i6 := 0 to key[6].ss do //if ks>=7 then

for i7 := 0 to key[7].ss do //if ks>=8 then

for i8 := 0 to key[8].ss do //if ks>=9 then

for i9 := 0 to key[9].ss do //if ks>=10 then

for i10 := 0 to key[10].ss do begin

inc(tot);

num[i,j,i1,i2,i3,i4,i5,i6,i7,i8,i9,i10] := tot;

a[tot].next := nil;

end;  //初始化,给分层图的节点编号

// writeln(tot);

 

buildgragh;

spfa(num[1,1,0,0,0,0,0,0,0,0,0,0]); //最初是没有任何钥匙的

 

ans := maxlongint;

// for i := 1 to 10 do writeln(key[i].ss);

for i1 := 0 to key[1].ss do

for i2 := 0 to key[2].ss do

for i3 := 0 to key[3].ss do

for i4 := 0 to key[4].ss do

for i5 := 0 to key[5].ss do

for i6 := 0 to key[6].ss do

for i7 := 0 to key[7].ss do

for i8 := 0 to key[8].ss do

for i9 := 0 to key[9].ss do

for i10 := 0 to key[10].ss do begin

// writeln(dis[num[n,m,i1,i2,i3,i4,i5,i6,i7,i8,i9,i10]]);

if dis[num[n,m,i1,i2,i3,i4,i5,i6,i7,i8,i9,i10]]<ans

then ans := dis[num[n,m,i1,i2,i3,i4,i5,i6,i7,i8,i9,i10]];

end;

//writeln(ans);

if ans=na then writeln(-1) else writeln(ans);

close(input);

close(output);

end.

posted on 2011-11-17 19:59  龙豆  阅读(545)  评论(0编辑  收藏  举报

导航