题目描述
沙丘 【问题描述】 根据新出土的一批史料记载,在塔克拉玛干沙漠中的一座沙丘下面,埋藏着一个神秘的地下迷宫。由著名探险家阿强率领的探险队经过不懈的挖掘,终于发现了通往地下迷宫的入口!队员们兴奋不已,急忙钻下去,去寻找那个埋藏已久的秘密。 他们刚钻进迷宫,只听“轰隆”一声巨响,回头一看,入口已与石墙融为一体,无法辨认。他们意识到自己被困在迷宫里了!环顾周围,似乎是一个洞穴。 这座迷宫由很多洞穴组成,某些洞穴之间有道路连接。每个洞穴都有一盏灯,凭借着微弱的灯光,可以看清有多少条道路与这个洞穴相连。每个洞穴的内部是完全相同的,且无法做标记。每条道路也是完全相同的,也无法做标记。 阿强凭借着微弱的灯光,发现了墙壁上的一段文字(事实上,每个洞穴的墙壁上都有这段文字),翻译成现代汉语就是:“陌生人,请把这个迷宫的洞穴数和道路数告诉我,我就会指引你走出迷宫。” 阿强很快镇定了下来,他拿出一个路标,对队员们说:“这个迷宫的危险程度远超出我们的想象,为了安全起见,大家一定要集体行动。我这儿有一个路标,有了它,我们一定能探明迷宫的结构。大家跟我走!” 现在,轮到你扮演阿强了。路标只有一个,可以随身携带,也可以暂时放在某个洞穴中(把路标放在道路上是毫无意义的,因为那里一片漆黑,什么都看不见)。你的任务很简单:用尽量少的步数探明这个迷宫共有多少个洞穴和多少条道路。“一步”是指从一个洞穴走到另一个相邻的洞穴。 【交互方法】 本题是一道交互式题目,你的程序应当和测试库进行交互,而不得访问任何文件(包括临时文件)。测试库提供了若干函数,它们的用法和作用如下: init必须先调用,但只能调用一次,用作初始化测试库; look(d, sign)的作用是查看当前洞穴的情况,测试库将从整型变量d中返回与该洞穴相连的道路的数目,从布尔变量sign中返回该洞穴内是否有路标,sign为true表示有路标,为false表示无路标。 put_sign的作用是在当前洞穴放上路标。只有当路标随身携带着的时候,才可以调用这个函数。 take_sign的作用是把当前洞穴的路标拿走。只有当路标在当前洞穴时,才可以调用这个函数。 walk(i)的作用是沿着编号为i的道路走到相邻的洞穴中。这里的编号是相对于当前所在洞穴而言的,并且是暂时的。假设与某洞穴相连的道路有d条,这些道路按照逆时针顺序依次编号为0,1,2,……,d-1。走第一步时,编号为0的道路由库确定。以后的过程,阿强会将他走进这个洞穴的道路编号为0。 report(n, m)的作用是向测试库报告结果。n表示洞穴的数目,m表示道路的数目。当这个函数被调用后,测试库会自动中止你的程序。 【对使用Pascal选手的提示】 你的程序应当使用如下的语句引用测试库。 uses dune_lib; 测试库使用的函数原型为: procedure init; procedure look(var d: longint; var sign: boolean); procedure put_sign; procedure take_sign; procedure walk(i: longint); procedure report(n, m: longint); 【对使用C/C++选手的提示】 你应当建立一个工程,把文件dune_libc.o包含进来,然后在程序头加一行: #include “dune_lib.h” 测试库使用的函数原型为: void init(); void look(int *, int *); void put_sign(); void take_sign(); void walk(int); void report(int, int); 在C/C++中,布尔型变量用整型变量代替,0表示false,1表示true。 【你如何测试自己的程序】 在工作目录下建立一个文件叫做dune.in,文件的第一行包括一个整数n为洞穴的数目,洞穴用1到n的整数编号,以下n行描述迷宫的结构。文件的第i+1行描述编号为i的洞穴的情况,第一个数di表示与该洞穴相连的道路的数目,其后的di个数按照逆时针顺序给出了这些道路另一端的洞穴编号。 调用init函数之后,库将编号为1的洞穴作为探险队的起始洞穴,并暂定编号为0的道路通向的洞穴编号为文件中第二行的第二个数。比如样例中,初始时,库暂定编号为0的道路通向洞穴4。 执行你的程序,此时测试库会产生输出文件dune.log,该文件中包括了你程序和库交互的记录和最后的结果。 如果程序正常结束,dune.log的最后一行包含一个整数,为你走的步数。 如果程序非法退出,则我们不保证dune.log中的内容有意义。 【约定】 洞穴数不超过100,道路数不超过4000。 迷宫是连通的,即任意两个洞穴都相互可达。 两个洞穴之间最多只有一条道路。 没有哪条道路连接两个相同的洞穴。 【样例】 dune.in内容如下 探险队初始时站在编号为1的洞穴内,编号为0的道路通向洞穴2,编号为1的道路通向洞穴3,编号为2的道路通向洞穴4。 一种可能得满分的调用方案如下: Pascal选手的调用方法 C/C++选手的调用方法 说明 init; init(); 初始化程序 look(d, sign); look(d, sign); 返回d=3,sign=false put_sign; put_sign(); 放下路标 walk(0); walk(0); 选择编号为0的道路 look(d, sign); look(d, sign); 返回d=2,sign=false walk(1); walk(1); 选择编号为1的道路 look(d, sign); look(d, sign); 返回d=2,sign=false walk(1); walk(1); 选择编号为1的道路 look(d, sign); look(d, sign); 返回d=3,sign=true take_sign; take_sign(); 拿起路标 walk(1); walk(1); 选择编号为1的道路 look(d, sign); look(d, sign); 返回d=2,sign=false walk(1); walk(1); 选择编号为1的道路 look(d, sign); look(d, sign); 返回d=1,sign=false report(5, 5); report(5, 5); 返回洞穴数为5,道路数为5 注意,该例子只是对库函数的使用说明,并没有算法上的意义。 【评分方法】 如果你的程序有下列情况之一,该测试点0分: 访问了任何文件(包括临时文件)或者自行终止; 非法调用库函数; 让测试库异常退出。 否则你每个测试点的得分按这样来计算: 如果你所报告的洞穴数与通道数都不正确,得0分;如果只有其中一个正确,得2分;如果两个都正确,则根据walk函数的调用次数评分,公式如下: 其中your_ans表示你的程序调用walk函数的次数,our_ans表示我们的程序的结果。
评分公式:
DFS+环处理
1 program dune; 2 uses dune_lib; 3 var 4 n,m,i:longint; 5 p:boolean; 6 d,next:array[0..200] of longint; 7 v:array[0..200,0..200] of boolean; 8 //========================= 9 procedure back(x:longint); 10 begin walk(d[x]-next[x]); end; 11 //========================= 12 procedure dfs(x:longint); 13 var 14 i,j,k,t:longint; 15 p:boolean; 16 begin 17 inc(n); look(d[x],p); next[x]:=0; 18 fillchar(v[x],sizeof(v[x]),0); 19 if p then take_sign; 20 for i:=1 to d[x]-1 do 21 if not v[x,i] then 22 begin 23 if next[x]>0 then 24 begin back(x); walk(0); end; 25 walk(i); next[x]:=i; 26 put_sign; walk(0); 27 p:=false; 28 for j:=x downto 2 do 29 begin 30 back(j); look(t,p); 31 if p then break; 32 end; 33 if p then take_sign; 34 walk(0); inc(m); 35 for k:=j to x-1 do walk(next[k]); 36 if p then 37 begin 38 put_sign; walk(0); 39 for k:=x-1 downto j do back(k); 40 for k:=next[j-1]+1 to d[j-1]-1 do 41 begin 42 walk(1); look(t,p); 43 if p then 44 begin 45 v[j-1,k]:=true; 46 break; 47 end; 48 walk(0); 49 end; 50 take_sign; 51 end else 52 begin 53 walk(i); dfs(x+1); 54 end; 55 end; 56 back(x); 57 end; 58 //========================= 59 begin 60 init; 61 n:=1; m:=0; look(d[1],p); 62 for i:=0 to d[1]-1 do 63 if not v[1,i] then 64 begin 65 walk(longint(i<>0)); next[1]:=i; 66 dfs(2); inc(m); 67 end; 68 report(n,m); 69 end.