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.
浙公网安备 33010602011771号