NOIP2011 Mayan游戏 题解

描述

 Mayan puzzle是最近流行起来的一个游戏。游戏界面是一个7行5列的棋盘,上面堆放着一些方块,方块不能悬空堆放,即方块必须放在最下面一行,或者放在其他方块之上。游戏通关是指在规定的步数内消除所有的方块,消除方块的规则如下:

1、每步移动可以且仅可以沿横向(即向左或向右)拖动某一方块一格:当拖动这一方块时,如果拖动后到达的位置(以下称目标位置)也有方块,那么这两个方块将交换位置(参见图6到图7);如果目标位置上没有方块,那么被拖动的方块将从原来的竖列中抽出,并从目标位置上掉落(直到不悬空,参见图1和图2);

2、任一时刻,如果在一横行或者竖列上有连续三个或者三个以上相同颜色的方块,则它们将立即被消除(参见图1到图3)。

注意:
a) 如果同时有多组方块满足消除条件,几组方块会同时被消除(例如下面图4,三个颜色为1的方块和三个颜色为2的方块会同时被消除,最后剩下一个颜色为2的方块)。

b) 当出现行和列都满足消除条件且行列共享某个方块时,行和列上满足消除条件的所有方块会被同时消除(例如下面图5所示的情形,5个方块会同时被消除)。

3、方块消除之后,消除位置之上的方块将掉落,掉落后可能会引起新的方块消除。注意:掉落的过程中将不会有方块的消除。

上面图1到图3给出了在棋盘上移动一块方块之后棋盘的变化。棋盘的左下角方块的坐标为(0, 0),将位于(3, 3)的方块向左移动之后,游戏界面从图1变成图2所示的状态,此时在一竖列上有连续三块颜色为4的方块,满足消除条件,消除连续3块颜色为4的方块后,上方的颜色为3的方块掉落,形成图3所示的局面。

格式

输入格式

第一行为一个正整数n,表示要求游戏关的步数。

接下来的5行,描述7*5的游戏界面。每行若干个整数,每两个整数之间用一个空格隔开,每行以一个0 结束,自下向上表示每竖列方块的颜色编号(颜色不多于10种,从1开始顺序编号,相同数字表示相同颜色)。

输入数据保证初始棋盘中没有可以消除的方块。

输出格式

如果有解决方案,输出n行,每行包含3个整数x,y,g,表示一次移动,每两个整数之间用一个空格隔开,其中(x,y)表示要移动的方块的坐标,g表示移动的方向,1表示向右移动,-1表示向左移动。注意:多组解时,按照x为第一关键字,y为第二关键字,1优先于-1,给出一组字典序最小的解。游戏界面左下角的坐标为(0, 0)。
如果没有解决方案,输出一行,包含一个整数-1。

样例1

样例输入1[复制]

3
1 0
2 1 0
2 3 4 0
3 1 0
2 4 3 4 0

样例输出1[复制]

2 1 1
3 1 1
3 0 1

限制

3s

提示

样例输入的游戏局面如图6到图11所示。依次移动的三步是:(2,1)处的方格向右移动,(3,1)处的方格向右移动,(3,0)处的方格向右移动,最后可以将棋盘上所有方块消除。

数据规模如下:
对于30%的数据,初始棋盘上的方块都在棋盘的最下面一行;
对于100%的数据,0 < n ≤ 5。

在做题时应注意:

(1)不用枚举整个棋盘,只需要每列都搜索到最高层即可。
(2)我们发现把(x,y)向右移和把(x+1,y)向左移是一样的,不用重复搜索,所以我们可以只向右交换,此外还应注意到空格不能移动,所以要判断这种特殊情况。
(3)应注意到可能出现以下状况:

NOIP2011 Mayan游戏 题解_第1张图片

即出现十字型。

(4)为了提高效率,防止一个点被算不止一次,所以要等到全部搜索完再统一消去。

(5)注意左右移动时要先判断是否下落。

先来一个30分的做法 :

program mayan;
type
  armap=array[0..7,0..5] of longint;
  tnode=record
  end;
var
  n:longint;//步数  
  map:armap;
procedure init;
begin
  assign(input,'mayan.in');
  assign(output,'mayan.out');
  reset(input);
  rewrite(output);
end;procedure outit;
begin
  close(input);
  close(output);
  halt;
end;
procedure readdata;
var
  i,num,x:longint;
begin
  read(n);
  num:=0;
  for i:=1 to 5 do
  begin
    read(x);
    while x<>0 do
    begin
      inc(num);
      map[num,i]:=x;
      read(x);
    end;
    num:=0;
  end;
 { for i:=1 to 7 do
  begin
    for x:=1 to 5 do
      if map[i,x]=0 then write('  ')
      else write(map[i,x],' ');
    writeln;
  end;}
end;
procedure percent30;
var
  i:longint;
  flag:longint;
  num:longint;
  k:array[1..3] of longint;//记录flag=3时的颜色状态
begin
  flag:=0;
  for i:=1 to 5 do
    if map[1,i]<>0 then
      inc(flag);//flag为1,2,4和5时必定无法消完
  if flag in [1,2,4,5] then
  begin
    writeln(-1);
    outit;
  end;
  //处理剩下flag=3的情况
  num:=0;
  for i:=1 to 5 do
    if map[1,i]<>0 then
    begin
      inc(num);
      k[num]:=map[1,i];
    end;
  if not((k[1]=k[2])or(k[2]=k[3])or(k[1]=k[3])) then
  begin
    writeln(-1);
    outit;
  end;
  //剩下只有第一行有三个并且颜色相同 且要在刚好steps步数下完成
  //(由于初始状态无可消除的 所以只有三种情况 打表。。。)
  for i:=1 to 5 do if map[1,i]<>0 then map[1,i]:=1 else map[1,i]:=0;//把有颜色的处理成1
  //三种情况分别为1 1 0 1 0      1 1 0 0 1     1 0 1 0 1
  if (map[1,1]=1)and(map[1,2]=1)and(map[1,3]=0)and(map[1,4]=1)and(map[1,5]=0) then
  begin//1 1 0 1 0
    if n=1 then
    begin
      writeln('3 0 -1');
      outit;
    end
    else if n=2 then
    begin
      writeln('1 0 1');
      writeln('0 0 1');
      outit;
    end
    else if n=4 then
    begin
      writeln('3 0 1');
      writeln('1 0 1');
      writeln('0 0 1');
      writeln('1 0 1');
      outit;
    end
    else begin
      writeln(-1);
      outit;
    end;
  end
  else if (map[1,1]=1)and(map[1,2]=1)and(map[1,3]=0)and(map[1,4]=0)and(map[1,5]=1) then
  begin//1 1 0 0 1
    if n=2 then
    begin
      writeln('4 0 -1');
      writeln('3 0 -1');
      outit;
    end
    else if n=3 then
    begin
      writeln('1 0 1');
      writeln('0 0 1');
      writeln('4 0 -1');
      outit;
    end
    else if n=4 then
    begin
      writeln('1 0 1');
      writeln('0 0 1');
      writeln('2 0 1');
      writeln('1 0 1');
      outit;
    end
    else begin
      writeln(-1);
      outit;
    end;
  end
  else if (map[1,1]=1)and(map[1,2]=0)and(map[1,3]=1)and(map[1,4]=0)and(map[1,5]=1) then
  begin//1 0 1 0 1
    if n=2 then
    begin
      writeln('0 0 1');
      writeln('4 0 -1');
      outit;
    end
    else if n=3 then
    begin
      writeln('0 0 1');
      writeln('2 0 1');
      writeln('1 0 1');
      outit;
    end
    else begin
      writeln(-1);
      outit;
    end;
  end
  else begin
    writeln(-1);
    outit;
  end;
end;
procedure main;
var
  i:longint;
  flag:boolean;
begin
  flag:=true;
  for i:=1 to 5 do
    if map[2,i]<>0 then
      flag:=false;//flag=true时表示第二行没有方块 即只有第一行有方块
  if flag then percent30;//处理30%的数据  都在最下面一行 即map[1,1~5]
  if not flag then writeln(-1);//剩下的直接cheat -1
end;
begin
  //init;
  readdata;
  main;
  //outit;
end.

满分做法:

program
type
  state=array[1..10,1..10] of longint;
  arr=array[1..10] of longint;


var
  map:state;len:arr;
  p:array[1..10] of record x,y,z:longint;end;
  n,i,j,tmp,t,tt,pt : longint;


procedure doit(x,y,g:longint;var map:state; var len:arr);
var
  tmp,i,j,k,color,t:longint; flag:boolean;
begin
  if g=-1 then
    begin tmp:=map[x,y];map[x,y]:=map[x-1,y];map[x-1,y]:=tmp;end
  else
    if g=1 then
      begin tmp:=map[x,y];map[x,y]:=map[x+1,y];map[x+1,y]:=tmp;end;
  if map[x,y]=0 then
    begin
      for j:=y downto 1 do
        if map[x+g,j]=0 then break;
      map[x+g,j]:=map[x+g,y]; map[x+g,y]:=0;len[x+g]:=j;
    end;
  
 
repeat
    flag:=false;
    for i:=1 to 5 do
      for j:=1 to 7 do
        begin
          if map[i,j]=0 then continue;
          color:=map[i,j];
          if i<=3 then
            begin
              t:=1;
              for k:=1 to 2 do
                if map[i+k,j]=color then inc(t);
              if t=3 then
                begin for k:=0 to 2 do map[i+k,j]:=0;flag:=true;end;
            end;
          if j>=3 then
            begin
              t:=1;
              for k:=1 to 2 do if map[i,j-k]=color then inc(t);
              if t=3 then
                begin for k:=0 to 2 do map[i,j-k]:=0;flag:=true;end;
            end;
        end;
    if flag then
      begin
        for i:=1 to 5 do
          begin
            for j:=1 to 7 do if map[i,j]=0 then break;
            for k:=j+1 to 7 do if map[i,k]<>0 then break;
            if map[i,k]=0 then continue
            else
              begin
                move(map[i,k],map[i,j],4*(len[i]-k+1));
                for j:=j+len[i]-k+1 to 7 do map[i,j]:=0;
              end;
          end;
        for i:=1 to 5 do
          begin
            for j:=1 to 7 do if map[i,j]=0 then break;
            len[i]:=j-1;
          end;
      end;
  until not flag;
end;


 
procedure dfs(max,depth:integer;map:state;len:arr);
var  bak:state; bak2:arr;i,j,zz,way:longint;
begin
  zz:=0;
  for i:=1 to 5 do inc(zz,len[i]);
  if zz=0 then
    begin
      for i:=1 to pt do writeln(p[i].x-1,' ',p[i].y-1,' ',p[i].z);  close(input);close(output);halt;
    end;
  if depth>max then exit;
  way:=0; inc(tt); bak:=map; bak2:=len;
  if tt>40000000 then begin writeln('-1');close(input);close(output);halt;end;
  for i:=1 to 5 do
    for j:=1 to len[i] do
      begin
        if i<5 then
          begin
            doit(i,j,1,map,len);inc(pt);
            p[pt].x:=i; p[pt].y:=j; p[pt].z:=1;
            dfs(max,depth+1,map,len);
            dec(pt); map:=bak; len:=bak2;
          end;
        if i>1 then
          begin
            doit(i,j,-1,map,len);inc(pt);          p[pt].x:=i;p[pt].y:=j;p[pt].z:=-1;
            dfs(max,depth+1,map,len);         dec(pt); map:=bak; len:=bak2;
          end;
      end;
end;


begin
  assign(input,'mayan.in');reset(input);  assign(output,'mayan.out');rewrite(output);
  readln(n);
  for i:=1 to 5 do
    begin
      t:=0;
      while not eoln do
        begin
          read(tmp);
          if tmp<>0 then begin inc(t);map[i,t]:=tmp;end;
          len[i]:=t;
        end;
      readln;
    end;
  dfs(n,1,map,len);
  writeln('-1');
  close(input); close(output);
end.


program mayan;
 typetyy=array[0..10,0..10] of longint;
 varf:tyy;
    n,i,a,b:longint;
    x,y,z:array[0..10] of longint;
 function jiancha(ff:tyy):boolean;                              //检查最低层是否都消去
  vari,j:longint;
 begin
  for i:=0 to 4 do
    if ff[i,0]<>0 then exit(false);
  exit(true)
 end;
 procedure print;
  vari:longint;
 begin
  for i:=1 to n do
   writeln(x[i],' ',y[i],' ',z[i]);
  {close(input); close(output);}
  halt;                                          //若有多种方案则以x为第一关键字按字典序输出,因为搜索时按x从0到4搜索的,故找到第一种方案是可以直接退出程序
 end;
 procedure dfs(k:longint; ff:tyy);
 forward;
 procedure check(k:longint; ff:tyy);
  varbt:array[0..4,0..7] of boolean;
     boo:boolean;
     i,j,kk,t:longint;
 begin
  boo:=false;
  while (not boo) do             //判断是否能下降,若没有下降则说明没有能消除的方块
   begin
    for i:=0 to 4 do for j:=0 to 7 do bt[i,j]:=false;               //初始化bt,记录能否消除
    for i:=0 to 4 do for j:=0 to 7 do                              //消除三个连续相同的
     begin
      if ff[i,j]=0 then continue;
      if (ff[i,j]=ff[i,j+1])and(ff[i,j+1]=ff[i,j+2])and(ff[i,j]<>0)then
       begin
        bt[i,j]:=true;
        bt[i,j+1]:=true;
        bt[i,j+2]:=true;
       end;
      if (ff[i,j]=ff[i+1,j])and(ff[i+1,j]=ff[i+2,j])and(ff[i,j]<>0)then
       begin
        bt[i,j]:=true;
        bt[i+1,j]:=true;
        bt[i+2,j]:=true;
       end;
     end;
    for i:=0 to 4 do for j:=0 to 7 do if bt[i,j] then ff[i,j]:=0;
    boo:=true;
    for i:=0 to 4 do                                                        //下降
     for j:=0 to 7 do
      if ff[i,j]=0 then
       begin
        t:=j;
        while (t<=7)and(ff[i,t]=0) do inc(t);
        if t>7 then break;
        for kk:=0 to t-j do
         begin
          ff[i,j+kk]:=ff[i,t+kk];
          ff[i,t+kk]:=0;
         end;
        boo:=false;
       end;
      if boo then break;
   end;
   if (k<>n+1)and(jiancha(ff)) then exit;                //若不是在规定步数走完,则退出
   if (k=n+1)and(jiancha(ff)) then print;                //若恰好在规定步数消完,则输出
   dfs(k,ff);
  end;
 procedure dfs(k:longint; ff:tyy);
  vari,j,tt:longint;
 begin
   ifk>n then exit;
  for j:=0 to 4 do
   for i:=0 to 7 do
    begin
     if (ff[j,i]=0)then break;
     if (ff[j-1,i]=0)and(j<>0)and(ff[j,i]<>ff[j-1,i]) then          //若可以交换,则尝试交换
      begin
       x[k]:=j;  y[k]:=i;  z[k]:=-1;
       tt:=ff[j,i];  ff[j,i]:=ff[j-1,i];  ff[j-1,i]:=tt;
       check(k+1,ff);
       tt:=ff[j,i];  ff[j,i]:=ff[j-1,i];  ff[j-1,i]:=tt;
       x[k]:=0;  y[k]:=0;  z[k]:=0;
      end;
     if j=4 then continue;          //若搜索到最后一列,则不能向右移动,退出本次循环
     if ff[j,i]=ff[j+1,i] then continue;
     x[k]:=j; y[k]:=i; z[k]:=1;
     tt:=ff[j,i]; ff[j,i]:=ff[j+1,i]; ff[j+1,i]:=tt;
     check(k+1,ff);
     tt:=ff[j,i]; ff[j,i]:=ff[j+1,i]; ff[j+1,i]:=tt;
     x[k]:=0; y[k]:=0; z[k]:=0;
    end;
 end;
 begin
  {assign(input,'mayan.in');reset(input);
  assign(output,'mayan.out');rewrite(output);}
  readln(n);
  for i:=0 to 4 do                                                     //读入数据
   begin
    b:=-1;
    repeat
     read(a);
     inc(b);
     f[i,b]:=a;
    until a=0;
   end;
  x[0]:=-1; y[0]:=-1; z[0]:=-2;                                 //将x、y、z数组初始化
  dfs(1,f);
  writeln('-1');                                           //若没有解决方案,输出-1
  {close(input);

 close(output);}

 end.


你可能感兴趣的:(NOIP解题报告)