POJ - 3074 Sudoku(DLX)
題目鏈接:點(diǎn)擊查看
題目大意:用更快的方法解數(shù)獨(dú)
題目分析:這個(gè)題和上一個(gè)題都是解數(shù)獨(dú),但時(shí)間限制少了一秒鐘,難度卻上升了一個(gè)檔次,上一個(gè)題用暴力dfs完全可以跑出來,不過要用1.5秒左右的時(shí)間,所以這個(gè)題目直接暴力跑肯定是不行的,我們可以考慮優(yōu)化一下,比如更巧妙的剪枝,或者用位運(yùn)算CSP(constraint satisfaction problem)來解決,不過我覺得最好的辦法還是用DLX算法,這是半年之前從網(wǎng)上就看到過的一個(gè)很厲害的算法,可以完美解決精確覆蓋問題,當(dāng)時(shí)看了別人的博客學(xué)了有一陣子但還是沒學(xué)懂,這兩天又碰到了這個(gè)數(shù)獨(dú)題,就決定去學(xué)一下,原理差不多搞明白了,主要是把模板學(xué)會了,然后就可以為所欲為的用模板了(菜雞行為),也算是對DLX的一個(gè)初步了解入門吧,實(shí)際上應(yīng)該將這個(gè)算法當(dāng)做一個(gè)工具來使用,而不是當(dāng)做一種固定的模板來使用,不然會很大程度上的限制住思維,從而陷入思維定式,現(xiàn)在說這個(gè)還有點(diǎn)遠(yuǎn)了,眼前的目標(biāo)就是用DLX解決掉最簡單的數(shù)獨(dú)問題吧。。
這里有個(gè)大佬的博客:DLX詳解
還有用DLX解數(shù)獨(dú)的思路:DLX解數(shù)獨(dú)
這里有必要解釋一下代碼中的encode和decode函數(shù),一開始我也是不太理解的,為什么增加節(jié)點(diǎn)的時(shí)候encode(0,i,j)什么的,最后輸出答案的時(shí)候卻是decode(x,y,v)?不應(yīng)該是decode(0,x,y)嗎?這都什么亂七八糟的,這里需要結(jié)合一下DLX這個(gè)算法來解釋一下,DLX精確覆蓋,我自己理解的定義是這樣的:
在一個(gè)01矩陣中選出某幾行,以列為單位,使得這幾行中的所有1所在的列的交集為?,所有1所在的列的并集為矩陣的寬度。
在看過上面兩個(gè)博客后,應(yīng)該不難理解上面這個(gè)定義的意思,那么我們在一開始初始化(init)的時(shí)候,輸入的那個(gè)n就是矩陣的寬度,也就是一共有多少列
隨后增加節(jié)點(diǎn),加入的節(jié)點(diǎn)的格式是這樣的AddNode(int r,int c),要求輸入的形參為行+列,列的話我們已經(jīng)規(guī)定好了誰是誰,那么行的話我們該怎么確定呢?因?yàn)獒槍τ跀?shù)獨(dú)這個(gè)題目,最大的行數(shù)只有9*9*9行,為什么呢?因?yàn)樽顗那闆r是一個(gè)空的矩陣,也就是一開始的數(shù)獨(dú)全都是0,那么每個(gè)點(diǎn)都有九種情況需要加入節(jié)點(diǎn),也就是(矩陣長度*矩陣寬度)*每個(gè)點(diǎn)的情況種類=9*9*9種,那么我們完全可以用九進(jìn)制來表示這樣一個(gè)數(shù),比如(x,y,v),就表示在點(diǎn)(x,y)的值為v,我們可以直接用這個(gè)九進(jìn)制的數(shù)來表示矩陣中的行,矩陣中的列我就不用過多贅述了,上面的博客已經(jīng)講得很清楚了
這樣一來,最后DLX求出的答案,也就是dlx.A數(shù)組中儲存的到底是什么呢,儲存的都是第幾行,針對于數(shù)獨(dú)這個(gè)題目,因?yàn)榭隙〞?1個(gè)數(shù)字才能組成9*9的數(shù)字矩陣,所以一定會有81個(gè)不同的行儲存在最后的答案中,也就是說最后解碼的是行的序列,而行的序列是由一個(gè)九進(jìn)制的數(shù)字來表示的(x,y,v),所以我們就解碼出來了在點(diǎn)(x,y)的值為v
過多的我就不贅述了,這個(gè)代碼跑了79ms,真的驚艷到我了
直接掛代碼吧:
#include<iostream> #include<cstdio> #include<string> #include<ctime> #include<cstring> #include<algorithm> #include<stack> #include<queue> #include<map> #include<sstream> using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=9*9*9+100;//行數(shù)const int M=9*9*4+100;//列數(shù)const int NODE=9*9*9*4+100;//節(jié)點(diǎn)數(shù)struct DancingLink{int n,s,ansd;//列數(shù) 節(jié)點(diǎn)總數(shù) int S[M],A[N],H[N];//S[]該列節(jié)點(diǎn)總數(shù) A[]答案 H[]行首指針 int L[NODE],R[NODE],U[NODE],D[NODE]; //L[],R[],U[],D[] 上下左右 int X[NODE],C[NODE];//X[] C[] 行列編號 void init(int n){//初始化 this->n=n;for(int i=0;i<=n;i++)U[i]=i,D[i]=i,L[i]=i-1,R[i]=i+1;R[n]=0,L[0]=n;s=n+1;memset(S,0,sizeof(S));memset(H,-1,sizeof(H));}void DelCol(int c){//刪除列 L[R[c]]=L[c];R[L[c]]=R[c];for(int i=D[c];i!=c;i=D[i])for(int j=R[i];j!=i;j=R[j])U[D[j]]=U[j],D[U[j]]=D[j],--S[C[j]];}void ResCol(int c){//恢復(fù)列 for(int i=U[c];i!=c;i=U[i])for(int j=L[i];j!=i;j=L[j])++S[C[j]],U[D[j]]=j,D[U[j]]=j;L[R[c]]=c,R[L[c]]=c;}void AddNode(int r,int c){//添加節(jié)點(diǎn) ++S[c],C[++s]=c,X[s]=r;D[s]=D[c],U[D[c]]=s,U[s]=c,D[c]=s;if(H[r]<0) H[r]=L[s]=R[s]=s;//行首節(jié)點(diǎn)else R[s]=R[H[r]],L[R[H[r]]]=s,L[s]=H[r],R[H[r]]=s;}bool dfs(int d){//深度,深搜遍歷 if(!R[0]){ansd=d;return true;}int c=R[0];for(int i=R[0];i;i=R[i]) if(S[i]<S[c]) c=i;DelCol(c);for(int i=D[c];i!=c;i=D[i]){A[d]=X[i];for(int j=R[i];j!=i;j=R[j]) DelCol(C[j]);if(dfs(d+1)) return true;for(int j=L[i];j!=i;j=L[j]) ResCol(C[j]);}ResCol(c);return false;}}dlx; int maze[10][10];char ans[100];int encode(int x,int y,int v) {return x*9*9+y*9+v+1; }void decode(int num,int &x,int &y,int &v) {num--;v=num%9;num/=9;y=num%9;num/=9;x=num%9; }int main() { // freopen("input.txt","r",stdin);string s;while(cin>>s&&s!="end"){for(int i=0;i<9;i++)for(int j=0;j<9;j++)maze[i][j]=s[i*9+j]=='.'?0:s[i*9+j]-'0';dlx.init(9*9*4);for(int i=0;i<9;i++)for(int j=0;j<9;j++)for(int k=0;k<9;k++)if(!maze[i][j]||maze[i][j]==k+1){int x=encode(i,j,k);dlx.AddNode(x,encode(0,i,j));dlx.AddNode(x,encode(1,i,k));dlx.AddNode(x,encode(2,j,k));dlx.AddNode(x,encode(3,i/3*3+j/3,k));}dlx.dfs(0);for(int i=0;i<dlx.ansd;i++){int x,y,v;decode(dlx.A[i],x,y,v);ans[x*9+y]=v+'0'+1;}ans[dlx.ansd]=0;printf("%s\n",ans);} return 0; }?
總結(jié)
以上是生活随笔為你收集整理的POJ - 3074 Sudoku(DLX)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 舞蹈链(DLX)模板
- 下一篇: HDU - 1427 速算24点(dfs