算法:搜索
很经典的一道题,也是很考验选手水平的一道题。
本题总共有两种方法:
费解的开关
题目本身是一个经典的广搜问题,题目读完后可以立即写出一个广搜的程序。但是,这只能通过30%的小数据;当n=500时做500次普通的广搜必然超时。或许,有人曾想过提高每一次广搜的效率,比如采用双向搜索。连双向搜索都已经想到了,为何不尝试一下反向搜索呢?
程序读入数据前我们需要一次广搜作为初始化。我们可以从最终状态出发,用一次广搜倒推出所有前6步的状态。这样,所有能够在6步以内完成的状态我们都求出来了。以后读入数据,问到哪个答哪个,如果读入的状态不在我们求出的状态中直接输出“-1”就行了。
在程序实现时,我们可以把状态压缩成25位的二进制数并用十进制储存。使用位运算将使得程序运行得更快。xor有一个很好的特性,一个Boolean值与True进行xor运算后将取反,与False进行xor运算后值不变。于是,对二进制数a的右数第4个数字取反,我们可以用a xor (1 shl 3)来完成。你完全可以把25种状态变化所对应的要xor的值先算出来。
有一个小地方值得注意:第三个数据出现了输出为0的情况,即某个初始状态全部为1。很多人在这里没有处理好。
补充
疾风剑客提供了一个相当快的算法,它的算法能在相当短的时间里算出给定状态到最终状态所需要的步数:对于每个状态,算法只需要枚举第一行改变哪些灯的状态,只要第一行的状态固定了,接下来的状态改变方法都是唯一的:每一行需要改变状态的位置都在上一行中不亮的灯的正下面,因为只有这样才能使上一行的灯全亮。我们枚举第一行的状态改变方法(共2^5种),对于每种方法都依次改变下面几行的状态使上面一行灯全亮。到最后一行我们需要判断是否最后一行也恰好全亮,并更新最小步数。好像这个算法能在0ms内出结果。
这题一开始的BFS我想到了,但是好像非常裸,直接就TLE了,后来看了某神的题解才知道既需要状态压缩还需要反向搜索。(PS:状态压缩想到了,反向没想到。)
对于存储状态我弄得是个很裸的线性表,某神用的强HASH,让我情何以堪啊~这说明自己对HASH的理解还不够深,主要是对于HASH的理解还很弱。
很经典的一道题,也是很考验选手水平的一道题。
本题总共有两种方法:
费解的开关
题目本身是一个经典的广搜问题,题目读完后可以立即写出一个广搜的程序。但是,这只能通过30%的小数据;当n=500时做500次普通的广搜必然超时。或许,有人曾想过提高每一次广搜的效率,比如采用双向搜索。连双向搜索都已经想到了,为何不尝试一下反向搜索呢?
程序读入数据前我们需要一次广搜作为初始化。我们可以从最终状态出发,用一次广搜倒推出所有前6步的状态。这样,所有能够在6步以内完成的状态我们都求出来了。以后读入数据,问到哪个答哪个,如果读入的状态不在我们求出的状态中直接输出“-1”就行了。
在程序实现时,我们可以把状态压缩成25位的二进制数并用十进制储存。使用位运算将使得程序运行得更快。xor有一个很好的特性,一个Boolean值与True进行xor运算后将取反,与False进行xor运算后值不变。于是,对二进制数a的右数第4个数字取反,我们可以用a xor (1 shl 3)来完成。你完全可以把25种状态变化所对应的要xor的值先算出来。
有一个小地方值得注意:第三个数据出现了输出为0的情况,即某个初始状态全部为1。很多人在这里没有处理好。
补充
疾风剑客提供了一个相当快的算法,它的算法能在相当短的时间里算出给定状态到最终状态所需要的步数:对于每个状态,算法只需要枚举第一行改变哪些灯的状态,只要第一行的状态固定了,接下来的状态改变方法都是唯一的:每一行需要改变状态的位置都在上一行中不亮的灯的正下面,因为只有这样才能使上一行的灯全亮。我们枚举第一行的状态改变方法(共2^5种),对于每种方法都依次改变下面几行的状态使上面一行灯全亮。到最后一行我们需要判断是否最后一行也恰好全亮,并更新最小步数。好像这个算法能在0ms内出结果。
这题一开始的BFS我想到了,但是好像非常裸,直接就TLE了,后来看了某神的题解才知道既需要状态压缩还需要反向搜索。(PS:状态压缩想到了,反向没想到。)
对于存储状态我弄得是个很裸的线性表,某神用的强HASH,让我情何以堪啊~这说明自己对HASH的理解还不够深,主要是对于HASH的理解还很弱。
另外对于本题的改变开关的操作使用位运算,xor是一种很好的运算。(和1做xor,都相同为0,不相同为1,其实用not也可以)
program vijos1197;
const
maxn=25;
maxzt=3000011;
dx:array [1..4] of -1..1=(0,-1,0,1);
dy:array [1..4] of -1..1=(-1,0,1,0);
type
atp=record
zt,dep:longint;
end;
var
start,n,ii,head,tail:longint;
que,dep,h:array [0..maxzt] of longint;{h为HASH数组,que存储状态,dep存储搜索深度。}
b:array [0..maxn] of longint;{b用来表示操作。}
function hash(x:longint):longint;{一种高效的用于存储状态的方法—HASH}
var
tmp,i:longint;
begin
tmp:=x mod maxzt;{HASH初始化。}
i:=tmp;
while (h[i]<>0) and (h[i]<>x) do{这里是确定状态的部分。}
begin
inc(i);
if i>=maxzt then i:=i mod maxzt;
end;
if h[i]=0 then h[i]:=x;
exit(i);
end;
procedure predo;
var
i,j,k,t,tx,ty:longint;
begin
head:=0;
tail:=1;
que[1]:=(1 shl 25)-1;
dep[hash(que[1])]:=1;
{生成转换的方式。}
for i:=1 to 5 do
begin
for j:=1 to 5 do
begin
t:=(i-1)*5+j;
b[t]:=b[t]+1 shl (25-t);
{这里操作之后,上下左右四个方向都需要改变。}
for k:=1 to 4 do
begin
tx:=i+dx[k];
ty:=j+dy[k];
if (tx in [1..5]) and (ty in [1..5]) then b[t]:=b[t]+1 shl (25-(tx-1)*5-ty);
end;
end;
end;
end;
procedure make;
var
x,i,xx:longint;
begin
while head<tail do
begin
inc(head);
x:=que[head];
if dep[hash(x)]<=6 then
begin
for i:=1 to 25 do
begin
xx:=x xor b[i];{25种转换方式。}
if dep[hash(xx)]=0 then
begin
dep[hash(xx)]:=dep[hash(x)]+1;
inc(tail);
que[tail]:=xx;
end;
end;
end;
end;
end;
procedure init;
var
i:longint;
ch:char;
begin
start:=0;
for i:=1 to 25 do
begin
read(ch);
start:=start+(ord(ch)-48) shl (25-i);
if i mod 5=0 then readln;
end;
end;
begin
assign(input,'VJ1197.in'); reset(input);
assign(output,'VJ1197.out'); rewrite(output);
readln(n);
predo;
make;
for ii:=1 to n do
begin
init;
if dep[hash(start)]=0 then writeln(-1) else writeln(dep[hash(start)]-1);{直接判断。}
readln;
end;
close(input); close(output);
end.