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;}