終於搞出個dancing links啦,跳舞慶祝。。
先簡單講講dancing links,大概就是處理一大類覆蓋問題的神法。
這個演算法處理的是一個叫做精確覆蓋的模型,意思大概就是給你個01矩陣,要求你選出若干行,使得每列有且僅有一個1
不同於別的搜尋,dancing links是搜每一列去被哪個行來覆蓋,用一個雙向十字鏈表來將所有的1鏈起來,從而達到去除冗餘操作,提升運行速度的神奇效果,療效顯著,3盒一個療程。。
好了,剛才有一點廢話,進入正題。(那個dancing links的具體實現可以參考一些論文或是代碼)
講講數獨怎麼轉化為精確覆蓋模型吧
可以想到,數獨無非就是帶有一大堆限制條件的選數問題嘛,精確覆蓋也就是有每列只能選一個的限制條件的選行問題,
那麼只要把這些限制條件對應到精確覆蓋上就可以了,怎麼對應呢?
以9數獨為例,每個格子理論上是可以填1~9之間的任意一個數,但由於有行列宮的限制,所以不能隨心所欲地填
而如果我們以 第i行第j列填k 對應到精確覆蓋中的行,那麼在第i行第j列填了k之後,第i行就不能再填k了,第j列也不能再填k了,第g[i,j]個宮也不能填k了,事實上,第i行第j列這個格子也不能再填別的數了
這可以聯想到精確覆蓋中每列有且僅有一個1吧!那麼我們把 第i行填k、第j列填k、第g[i,j]宮填k 還有 第i行第j列這個格子 對應到精確精確覆蓋中的列,再把 第i行第j列填k 這樣的行中 對應的列放上1(也就是在 第i行填k、第j列填k、第g[i,j]宮填k 還有 第i行第j列這個格子 這些列放1),恰好契合了數獨的所有限制條件,就可以用精確覆蓋模型做了!
dancing links=高效+極速,這話不假。
數獨實測,就算是加了一大堆最佳化的直接搜尋(包括某年集訓隊論文提到的一個帶狀態壓縮最佳化的那個版本)在dancing links面前也是抬不起頭的。
真的太神了~~
代碼:(代碼大同小異,這裡就貼個16數獨的算了)
pku 3076
program syj; {sudoku_dancing links}<br />const nn=16; oo=maxlongint shr 1;<br />var u,d,l,r,h,tx,ty,tz:array[0..(nn+1)*nn*nn*4]of longint;<br /> size:array[1..nn*nn*4]of longint;<br /> map:array[1..nn,1..nn]of longint;<br /> n,m,tot:longint; ok:boolean;<br />procedure print;<br />var i,j:longint;<br />begin<br /> for i:=1 to n do begin<br /> for j:=1 to n do write(chr(map[i,j]+64));<br /> writeln;<br /> end;<br /> writeln;<br />// writeln; inc(tt);<br />// writeln(tt);<br />// close(input);close(output);<br />// halt;<br /> ok:=true;<br />end;<br />procedure cover(i:longint);<br />var j,k:longint;<br />begin<br /> l[r[i]]:=l[i];r[l[i]]:=r[i]; j:=d[i];<br /> while j<>i do begin<br /> k:=r[j];<br /> while k<>j do begin<br /> u[d[k]]:=u[k];d[u[k]]:=d[k]; dec(size[h[k]]);<br /> k:=r[k];<br /> end;<br /> j:=d[j];<br /> end;<br />end;<br />procedure recover(i:longint);<br />var j,k:longint;<br />begin<br /> l[r[i]]:=i;r[l[i]]:=i; j:=u[i];<br /> while j<>i do begin<br /> k:=l[j];<br /> while k<>j do begin<br /> u[d[k]]:=k;d[u[k]]:=k; inc(size[h[k]]);<br /> k:=l[k];<br /> end;<br /> j:=u[j];<br /> end;<br />end;<br />procedure dlx;<br />var i,j,k,min:longint;<br />begin<br /> i:=r[0]; min:=oo<br /> while i<>0 do begin<br /> if size[i]<=min then begin //!! <=<br /> min:=size[i]; j:=i;<br /> end;<br /> i:=r[i];<br /> end;<br /> if min=0 then exit;<br /> if min=oo then begin<br /> print;exit;<br /> end;<br /> cover(j); i:=d[j];<br /> while i<>j do begin<br /> k:=r[i]; map[tx[i],ty[i]]:=tz[i];<br /> while k<>i do begin<br /> cover(h[k]); map[tx[k],ty[k]]:=tz[k];<br /> k:=r[k];<br /> end;<br /> dlx; if ok then exit;<br /> k:=l[i]; map[tx[i],ty[i]]:=0;<br /> while k<>i do begin<br /> recover(h[k]); map[tx[k],ty[k]]:=0;<br /> k:=l[k];<br /> end;<br /> i:=d[i];<br /> end;<br /> recover(j);<br />end;<br />function calc(i,j:longint):longint;<br />begin calc:=(i-1)div m*m+(j+m-1) div m; end;<br />procedure init;<br />var i,j,k,ii,kk:longint;<br /> cc:char;<br />begin<br /> tot:=n*n*4;<br /> for i:=1 to tot do begin<br /> l[i]:=i-1;r[i]:=i+1;u[i]:=i;d[i]:=i;<br /> end;<br /> r[0]:=1; r[tot]:=0; ii:=tot;<br /> for i:=1 to n do<br /> for j:=1 to n do<br /> for k:=1 to n do begin<br /> inc(tot); tx[tot]:=i;ty[tot]:=j;tz[tot]:=k; h[tot]:=(i-1)*n+k;inc(size[h[tot]]);<br /> l[tot]:=tot+3;r[tot]:=tot+1;u[tot]:=u[h[tot]];d[tot]:=h[tot];<br /> d[u[h[tot]]]:=tot;u[h[tot]]:=tot;<br /> inc(tot); tx[tot]:=i;ty[tot]:=j;tz[tot]:=k; h[tot]:=(j-1)*n+k+n*n;inc(size[h[tot]]);<br /> l[tot]:=tot-1;r[tot]:=tot+1;u[tot]:=u[h[tot]];d[tot]:=h[tot];<br /> d[u[h[tot]]]:=tot;u[h[tot]]:=tot;<br /> inc(tot); tx[tot]:=i;ty[tot]:=j;tz[tot]:=k; h[tot]:=(calc(i,j)-1)*n+k+n*n*2;inc(size[h[tot]]);<br /> l[tot]:=tot-1;r[tot]:=tot+1;u[tot]:=u[h[tot]];d[tot]:=h[tot];<br /> d[u[h[tot]]]:=tot;u[h[tot]]:=tot;<br /> inc(tot); tx[tot]:=i;ty[tot]:=j;tz[tot]:=k; h[tot]:=(i-1)*n+j+n*n*3;inc(size[h[tot]]);<br /> l[tot]:=tot-1;r[tot]:=tot-3;u[tot]:=u[h[tot]];d[tot]:=h[tot];<br /> d[u[h[tot]]]:=tot;u[h[tot]]:=tot;<br /> end;<br /> for i:=1 to n do begin<br /> for j:=1 to n do begin<br /> read(cc);if cc='-' then continue;<br /> k:=ord(cc)-64;<br /> kk:=4*((i-1)*n*n+(j-1)*n+k-1)+ii;<br /> for kk:=kk+1 to kk+4 do begin<br /> map[tx[kk],ty[kk]]:=tz[kk];<br /> cover(h[kk]);<br /> end;<br /> end;<br /> readln;<br /> end;<br />end;<br />begin<br /> n:=nn; m:=round(sqrt(n));<br /> while not seekeof do begin<br /> ok:=false;tot:=0;<br /> fillchar(size,sizeof(size),0);<br /> fillchar(map,sizeof(map),0);<br /> init;<br /> dlx;<br /> readln;<br /> end;<br />end.<br />
可惜,速度還是比盾盾那個詭異動態版dlx慢一點
還有,那個每次選1最少的那個列的那個地方,如果<=就是400ms左右,<就是3000ms+了
因為我的列的順序是行->列->宮->格子,估計是後面的宮的那個限制條件卡的緊一些,所有先選後面的會好些,這個地方囧了我好久