簡單易懂的Dancing links講解(3)

來源:互聯網
上載者:User

Dancing Links除了能解決精確覆蓋問題,還能解決重複覆蓋問題,這裡重點講重複覆蓋

題目:高手做題                                  

描述                                 

SubRaY被布置了n道作業題,可是他一道也不會..但他知道有w位高手,並知道每位高手                             

會做哪些題,請問SubRaY至少請多少位高手,才能把所有的題都做出來?                              

                                  

輸入                                 

第一行兩個整數n,w表示有n道作業題和w位高手,作業題以1..n編號.接下來w行,第i+1                                 

行第一個數li表示第i位高手會做的題目的數量,接下來li個數表示第i位高手會做哪                                  

些題目.                            

3<=n,w<=60,1<=li<=6                                

                                  

輸出                                 

一個數,SubRaY至少要請多少位高手.                                

                                  

範例輸入                                

4 4                             

2 1 2                                 

1 4                             

3 2 3 4                              

2 1 3                                                          

範例輸出                                

2     

代碼參考:http://blog.sina.com.cn/s/blog_51cea4040100gwpv.html      

為了詳細講解Dancing links 重複覆蓋的過程,我們先把範例輸入轉換為如下01矩陣

和精確覆蓋一樣,先選擇含1最少的列,這裡選擇的1行1列的,選擇後,

第1和2列被刪除了,第一步, 效果如下:

選擇3行3列後,第3,4列也被刪除了,至此重複覆蓋的一個解已經被找到了,即1,3行,

但還不能確定這個解是最優的(可能只需一行就可以把所有列都覆蓋),還需要繼續搜尋,

第二步

第三步,開始回溯,選擇3行3列的兄弟節點4行3列後的效果如下

那樣不能得到解,繼續回溯到如下效果,第四步

第五步

第六步

至此重複覆蓋的另一個解已經被找到了,即3行和4行,    

現在,所有的節點都被遍曆過了,這個重複覆蓋總共有兩個解;1行和3行,3行和4行,每個解都是最優的,都需要兩個高手

可供參考的剪枝函數:

int Hash(){    int ans=0;    bool hash[maxn]={0};    for (int c=R[0];c!=0;c=R[c]) {        if (!hash[c])        {            hash[c]=1;            ans++;            for (int i=D[c];i!=c;i=D[i])                for (int j=R[i];j!=i;j=R[j])                    hash[nCol[j]]=1;        } }cout << "Hash =>" << ans << endl;    return ans;}

這個函數實際上是在對目前狀態進行重複覆蓋,只是覆蓋列時,不是刪除列,而是把相應列的標誌位置1       ,這個函數的目的是,預先估計當前這樣選擇後,還需要多少行才能覆蓋所有列。  

函數傳回值越大,越可能被剪枝

Dancing links 精確覆蓋和重複覆蓋的區別

1.精確覆蓋更能體現dancing links 的威力,因為在剪枝的時候,精確覆蓋不僅對列剪枝,對行也進行了剪枝,                          

       而重複覆蓋只對列進行剪枝,要想提高重複覆蓋的效率還需要自己寫剪枝函數                        

2.重複覆蓋問題一般是求解最優解的,不像精確覆蓋找到一個解就算完事,因此重複覆蓋需要遍曆和考察所有的分支,來找到最優的

全部代碼:

#include<iostream>using namespace std;const int maxn=20;int L[maxn],R[maxn],U[maxn],D[maxn];int S[maxn]={0};int nCol[maxn];int nRow[maxn];bool answer[4]={0};int n,w;int best=INT_MAX;int sample[4][4] = {                  {1,1,0,0},                 {0,0,0,1},                  {0,1,1,1},                 {1,0,1,0}                };void init(){    n=4;w=4;    for (int i=0;i<=n;i++)    {        L[i]=i-1; R[i]=i+1;        U[i]=D[i]=i;}    L[0]=n; R[n]=0;    int cnt=n+1;    for (int i=0;i<w;i++)    {        int head=cnt,tail=cnt;        for (int j=0;j<n;j++)        {            int c = j+1;if(sample[i][j]==1){S[c]++;nCol[cnt]=c;nRow[cnt]=i;U[D[c]]=cnt;D[cnt]=D[c];U[cnt]=c;D[c]=cnt;L[cnt]=tail; R[tail]=cnt;R[cnt]=head; L[head]=cnt;tail=cnt;cnt++;}        }    }}void Remove(int x){cout << "remove=>" << x << endl;    for (int i=D[x];i!=x;i=D[i])    {        L[R[i]]=L[i];        R[L[i]]=R[i];        S[nCol[i]]--;    }}void Resume(int x){cout << "Resume=>" << x << endl;    for (int i=U[x];i!=x;i=U[i])    {        L[R[i]]=R[L[i]]=i;        S[nCol[i]]++;    }}int Hash(){    int ans=0;    bool hash[maxn]={0};    for (int c=R[0];c!=0;c=R[c]){        if (!hash[c])        {            hash[c]=1;            ans++;            for (int i=D[c];i!=c;i=D[i])                for (int j=R[i];j!=i;j=R[j])                    hash[nCol[j]]=1;        }}cout << "Hash =>" << ans << endl;    return ans;}void dfs(int ans){int best2 = ans + Hash();    if (best2>=best) return;    if (R[0]==0)    {        best=ans;for (int i = 0; i < 4; i++) {if ( answer[ i ] ) {   for (int j = 0; j < 4; j++) cout << sample[ i ][ j ] << " ";   cout << endl;}}        return;    }    int c,minnum=INT_MAX;    for (int i=R[0];i!=0;i=R[i])    {        if (S[i]==0) return;        if (S[i]<minnum)        {            minnum=S[i];            c=i;        }    }    for (int i=U[c];i!=c;i=U[i])    {answer[nRow[i]]=true;        Remove(i);        for (int j=R[i];j!=i;j=R[j])            Remove(j);        dfs(ans+1);        for (int j=L[i];j!=i;j=L[j])            Resume(j);        Resume(i);answer[nRow[i]]=false;    }}int main(){   // freopen("data.in","r",stdin);   // freopen("data.out","w",stdout);    init();    dfs(0);   printf("%d\n",best);   // fclose(stdin);   // fclose(stdout);getchar();    return 0;}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.