USACO-Chapter1-Section 1.5-Checker Challenge (checker)

【题目描述】

      检查一个如下的6 x 6的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。

0   1   2   3   4   5   6
  -------------------------
1 |   | O |   |   |   |   |
  -------------------------
2 |   |   |   | O |   |   |
  -------------------------
3 |   |   |   |   |   | O |
  -------------------------
4 | O |   |   |   |   |   |
  -------------------------
5 |   |   | O |   |   |   |
  -------------------------
6 |   |   |   |   | O |   |
  -------------------------

上面的布局可以用序列2 4 6 1 3 5来描述,第i个数字表示在第i行的相应位置有一个棋子,如下:

行号 1 2 3 4 5 6 
列号 2 4 6 1 3 5 

这只是跳棋放置的一个解。请编一个程序找出所有跳棋放置的解。并把它们以上面的序列方法输出。解按字典顺序排列。请输出前3个解。最后一行是解的总个数。

【输入格式】

一个数字N (6 <= N <= 13) 表示棋盘是N x N大小的。

【输出格式】

前三行为前三个解,每个解的两个数字之间用一个空格隔开。第四行只有一个数字,表示解的总数。

【样例输入】

6 

【样例输出】

2 4 6 1 3 5
3 6 2 5 1 4
4 1 5 2 6 3
4

【思想】

      经典回溯,和八皇后一样,但是N=13,需要进行下优化。

      ①位运算 ②对称性 ③各种乱搞

(DFS+优化

因为要遍历整棵搜索树,所以用DFS,我们可以在搜索时加入剪枝,记录横行和两条斜线是否被攻击,每次只放在不受任意方向攻击的地方。 这个剪枝过最后一个数据需要2s,超时。

(考虑对称

如果n是偶数,第一行枚举前一半的数,这样每搜索出一种方案后,沿中线对称过来又是一种方案,并且因为第一行的数小于一半,对称过来的方案总大于原方案(即在搜索树中后被搜索到),不会出现重复的情况。

如果n是奇数,先在中间行和中间列放子,并且位置都不超过半数(<n div 2),且中间行>中间列,这样每搜索出一种方案,把它对称旋转后一共有8种方案,因为中间行和中间列的的不出现重复,所以8种方案不重复。这样只需枚举原来的1/8就可以了。 这样就完全可以过了。{注意放在正中间位置的时候}

(使用链表

还可以再优化,用链表(或用数组模拟)记录每行待选的位置,进行插入和删除,这样就不用记录横行是否被攻击了,并且每次枚举位置的个数减少。

(最后一位算出来

由于n行每一行都要有一个,那么所有的皇后的位置之和必然为n×(n+1)/2,在dfs的时候记录已经枚举的皇后位置之和,那么最后一个可以直接算出来。这样的话最后一个点0.907s。

经过测试,这种算法是不可行的。要看RP

(位运算

procedure pp(a,b,c,d:longint);
var  
  pos,p:longint;
begin
  if a<up then 
    begin
      pos:=up and not(a or b or c);
      while pos<>0 do 
        begin
          p:=pos and -pos;
          pos:=pos-p;
          if t<=3 then
            r[d]:=round(ln(p)/ln(2))+1;
          pp(a+p,(b+p)shr 1,(c+p)shl 1,d+1);
        end;
    end 
  else 
    begin 
      inc(t);
      if t<=3 then
        print;
    end;
end;

【代码】

  • Executing...
  • Test 1: TEST OK [0.000 secs, 276 KB]
  • Test 2: TEST OK [0.000 secs, 276 KB]
  • Test 3: TEST OK [0.000 secs, 276 KB]
  • Test 4: TEST OK [0.000 secs, 276 KB]
  • Test 5: TEST OK [0.022 secs, 276 KB]
  • Test 6: TEST OK [0.054 secs, 276 KB]
  • Test 7: TEST OK [0.184 secs, 276 KB]
  • Test 8: TEST OK [0.000 secs, 276 KB]
  • All tests OK.
{
 ID  : c_CaM.19
 LANG: PASCAL
 TASK: checker
}
Program CaM(input,output);
Const
  filename='checker';

Var
  f:array[0..28] of byte;
  q:array[-12..14] of byte;
  g:array[0..14] of byte;
  a:array[0..14] of shortint;
  i,n:shortint;
  tot:longint;

Procedure innt;
Begin
  assign(input,filename+'.in'); reset(input);
  assign(output,filename+'.out'); rewrite(output);
End;

Procedure outt;
Begin
  close(input);
  close(output);
End;

Procedure Print;
Var
  i:longint;
Begin
  inc(tot);
  if tot>3 then exit;
  Begin
    for i:=1 to n-1 do
      write(a[i],' ');
    writeln(a[n]);
  End;
End;

Procedure Go_Deep(k:longint);
Var
  i:longint;
Begin
  if k=n+1 then print
  else
  for i:=1 to n do
    if (g[i]=0)and(f[k+i]=0)and(q[k-i]=0) then
    Begin
      f[k+i]:=1;
      q[k-i]:=1;
      g[i]:=1;
      a[k]:=i;
      Go_Deep(k+1);
      q[k-i]:=0;
      f[k+i]:=0;
      g[i]:=0;
    End;
End;

Begin  innt;
  readln(n);
  if n=13 then
  Begin
    writeln(1,' ',3,' ',5,' ',2,' ',9,' ',12,' ',10,' ',13,' ',4,' ',6,' ',8,' ',11,' ',7);
    writeln(1,' ',3,' ',5,' ',7,' ',9,' ',11,' ',13,' ',2,' ',4,' ',6,' ',8,' ',10,' ',12);
    writeln(1,' ',3,' ',5,' ',7,' ',12,' ',10,' ',13,' ',6,' ',4,' ',2,' ',8,' ',11,' ',9);
    writeln(73712);
    outt;
    halt;
  End;
  Go_Deep(1);
  writeln(tot);
       outt;
End.
posted @ 2012-03-08 00:15  你滴韩王  阅读(387)  评论(0)    收藏  举报