简单易懂的Dancing links讲解(3)

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行,每个解都是最优的,都需要两个高手

可供参考的剪枝函数:

[cpp]  view plain copy
  1. int Hash()        
  2. {         
  3.     int ans=0;        
  4.     bool hash[maxn]={0};          
  5.     for (int c=R[0];c!=0;c=R[c])          
  6.  {        
  7.         if (!hash[c])         
  8.         {         
  9.             hash[c]=1;        
  10.             ans++;        
  11.             for (int i=D[c];i!=c;i=D[i])          
  12.                 for (int j=R[i];j!=i;j=R[j])          
  13.                     hash[nCol[j]]=1;          
  14.         }         
  15.  }        
  16.     cout << "Hash =>" << ans << endl;        
  17.     return ans;       
  18. }  

这个函数实际上是在对当前状态进行重复覆盖,只是覆盖列时,不是删除列,而是把相应列的标志位置1       ,这个函数的目的是,预先估计当前这样选择后,还需要多少行才能覆盖所有列。  

函数返回值越大,越可能被剪枝

Dancing links 精确覆盖和重复覆盖的区别

1.精确覆盖更能体现dancing links 的威力,因为在剪枝的时候,精确覆盖不仅对列剪枝,对行也进行了剪枝,                          

       而重复覆盖只对列进行剪枝,要想提高重复覆盖的效率还需要自己写剪枝函数                        

2.重复覆盖问题一般是求解最优解的,不像精确覆盖找到一个解就算完事,因此重复覆盖需要遍历和考察所有的分支,来找到最优的


全部代码:

[cpp]  view plain copy
  1. #include<iostream>  
  2. using namespace std;  
  3. const int maxn=20;  
  4. int L[maxn],R[maxn],U[maxn],D[maxn];  
  5. int S[maxn]={0};  
  6. int nCol[maxn];  
  7. int nRow[maxn];  
  8. bool answer[4]={0};  
  9. int n,w;  
  10. int best=INT_MAX;  
  11.   
  12. int sample[4][4] = {   
  13.                  {1,1,0,0},  
  14.                  {0,0,0,1},   
  15.                  {0,1,1,1},  
  16.                  {1,0,1,0}  
  17.                 };  
  18.   
  19. void init()  
  20. {  
  21.     n=4;  
  22.     w=4;  
  23.     for (int i=0;i<=n;i++)  
  24.     {  
  25.         L[i]=i-1; R[i]=i+1;  
  26.         U[i]=D[i]=i;  
  27.     }  
  28.     L[0]=n;   
  29.     R[n]=0;  
  30.     int cnt=n+1;  
  31.     for (int i=0;i<w;i++)  
  32.     {  
  33.         int head=cnt,tail=cnt;  
  34.         for (int j=0;j<n;j++)  
  35.         {  
  36.             int c = j+1;  
  37.             if(sample[i][j]==1)  
  38.             {  
  39.                 S[c]++;  
  40.                 nCol[cnt]=c;  
  41.                 nRow[cnt]=i;  
  42.                 U[D[c]]=cnt;  
  43.                 D[cnt]=D[c];  
  44.                 U[cnt]=c;  
  45.                 D[c]=cnt;  
  46.                 L[cnt]=tail; R[tail]=cnt;  
  47.                 R[cnt]=head; L[head]=cnt;  
  48.                 tail=cnt;  
  49.                 cnt++;  
  50.             }  
  51.         }  
  52.     }  
  53. }  
  54. void Remove(int x)  
  55. {  
  56.     cout << "remove=>" << x << endl;  
  57.     for (int i=D[x];i!=x;i=D[i])  
  58.     {  
  59.         L[R[i]]=L[i];  
  60.         R[L[i]]=R[i];  
  61.         S[nCol[i]]--;  
  62.     }  
  63. }  
  64. void Resume(int x)  
  65. {  
  66.     cout << "Resume=>" << x << endl;  
  67.     for (int i=U[x];i!=x;i=U[i])  
  68.     {  
  69.         L[R[i]]=R[L[i]]=i;  
  70.         S[nCol[i]]++;  
  71.     }  
  72. }  
  73. int Hash()  
  74. {  
  75.     int ans=0;  
  76.     bool hash[maxn]={0};  
  77.     for (int c=R[0];c!=0;c=R[c])  
  78.     {  
  79.         if (!hash[c])  
  80.         {  
  81.             hash[c]=1;  
  82.             ans++;  
  83.             for (int i=D[c];i!=c;i=D[i])  
  84.                 for (int j=R[i];j!=i;j=R[j])  
  85.                     hash[nCol[j]]=1;  
  86.         }  
  87.     }  
  88.     cout << "Hash =>" << ans << endl;  
  89.     return ans;  
  90. }  
  91. void dfs(int ans)  
  92. {  
  93.     int best2 = ans + Hash();  
  94.     if (best2>=best)   
  95.         return;  
  96.     if (R[0]==0)  
  97.     {  
  98.         best=ans;  
  99.         for (int i = 0; i < 4; i++)   
  100.         {  
  101.             if ( answer[ i ] )   
  102.             {  
  103.                for (int j = 0; j < 4; j++) cout << sample[ i ][ j ] << " ";  
  104.                cout << endl;  
  105.             }  
  106.         }  
  107.         return;  
  108.     }  
  109.     int c,minnum=INT_MAX;  
  110.     for (int i=R[0];i!=0;i=R[i])  
  111.     {  
  112.         if (S[i]==0) return;  
  113.         if (S[i]<minnum)  
  114.         {  
  115.             minnum=S[i];  
  116.             c=i;  
  117.         }  
  118.     }  
  119.     for (int i=U[c];i!=c;i=U[i])  
  120.     {  
  121.         answer[nRow[i]]=true;  
  122.         Remove(i);  
  123.         for (int j=R[i];j!=i;j=R[j])  
  124.             Remove(j);  
  125.         dfs(ans+1);  
  126.         for (int j=L[i];j!=i;j=L[j])  
  127.             Resume(j);  
  128.         Resume(i);  
  129.         answer[nRow[i]]=false;  
  130.     }  
  131. }  
  132. int main()  
  133. {  
  134.    // freopen("data.in","r",stdin);  
  135.    // freopen("data.out","w",stdout);  
  136.     init();  
  137.     dfs(0);  
  138.        printf("%d\n",best);  
  139.    // fclose(stdin);  
  140.    // fclose(stdout);  
  141.     getchar();  
  142.     return 0;  
  143. }  

你可能感兴趣的:(Links,讲解,Dancing)