舞蹈鏈..Exact cover&Treasure Map等 . DLX-精確覆蓋

http://acm.hust.edu.cn/vjudge/contest/view.action?cid=65998#problem/A

Description
There is an N*M matrix with only 0s and 1s, (1 <= N,M <= 1000). An exact cover is a selection of rows such that every column has a 1 in exactly one of the selected rows. Try to find out the selected rows.
Input
There are multiply test cases. First line: two integers N, M; The following N lines: Every line first comes an integer C(1 <= C <= 100), represents the number of 1s in this row, then comes C integers: the index of the columns whose value is 1 in this row.
Output
First output the number of rows in the selection, then output the index of the selected rows. If there are multiply selections, you should just output any of them. If there are no selection, just output “NO”.
Sample Input
6 7
3 1 4 7
2 1 4
3 4 5 7
3 3 5 6
4 2 3 6 7
2 2 7
Sample Output
3 2 4 6

跟着bin神的專題刷的…

舞蹈鏈的第一個題呀!直接用上舞蹈鏈的精確覆蓋的模板就可以了, 因爲這個並沒有什麼構圖, 就是給定的01矩陣求覆蓋.

學習舞蹈鏈,可以看bin神給的blog…模板我也是用bin神的.

bin神的模板跟大白書上面有略有不同, 不過看懂了之後不難理解.

http://www.cnblogs.com/grenet/p/3145800.html
http://blog.csdn.net/mu399/article/details/7627862
上面兩位大牛寫的很清晰易懂..

通俗地說一下舞蹈鏈的應用就是:
行是自己擁有策略
列是要完成的任務目標
這樣就構成了一個01矩陣
最終要完成把所有的任務目標填爲1
如果使精確覆蓋就是每一列的1不可以重複,重複覆蓋即可以重複的.(兩者的模板略有不同, 主要是選中列後對列的刪除操作不一樣)

#include <cmath>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <string>
#include <map>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <iostream>
#define LL long long
#define pb push_back
#define lb lower_bound
#define eps 1e-8
#define INF 0x3f3f3f3f
using namespace std;
const int N = 1000;
const int maxnode = N*N+N+50;
struct DLX {
    int n,m,size;
    /// 上下右左指針   row 行 col 列 結點編號
    int U[maxnode],D[maxnode],R[maxnode],L[maxnode],Row[maxnode],Col[maxnode];
    int H[N+50],S[N+50];/// H 行頭 S 每列結點計數
    int ansd,ans[N+50];/// ansd 答案計數 ans 記錄答案
    void init(int _n,int _m) {
        n = _n; m = _m;
        for(int i = 0;i <= m;i++) {
            S[i] = 0;
            U[i] = D[i] = i;
            L[i] = i-1;
            R[i] = i+1;
        }
        R[m] = 0; L[0] = m;
        size = m;
        for(int i = 1;i <= n;i++)H[i] = -1;
    }
    void Link(int r,int c) {
        ++S[Col[++size]=c];
        Row[size] = r;
        D[size] = D[c];
        U[D[c]] = size;
        U[size] = c;
        D[c] = size;
        if(H[r] < 0)H[r] = L[size] = R[size] = size;
        else {
            R[size] = R[H[r]];
            L[R[H[r]]] = size;
            L[size] = H[r];
            R[H[r]] = size;
        }
    }
    void remove(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[Col[j]];
            }
    }
    void resume(int c) {
        for(int i = U[c];i != c;i = U[i])
            for(int j = L[i];j != i;j = L[j])
                ++S[Col[U[D[j]]=D[U[j]]=j]];
        L[R[c]] = R[L[c]] = c;
    }
    bool Dance(int d) {
        if(R[0] == 0) {
            ansd = d;
            printf("%d", ansd);
            for(int i=0; i<ansd; i++) printf(" %d", ans[i]);
            puts("");
            return true;
        }
        int c = R[0];
        for(int i = R[0];i != 0;i = R[i])
            if(S[i] < S[c])
                c = i;
        remove(c);
        for(int i = D[c];i != c;i = D[i]) {
            ans[d] = Row[i];
            for(int j = R[i];j != i;j = R[j])remove(Col[j]);
            if(Dance(d+1))return true;
            for(int j = L[i];j != i;j = L[j])resume(Col[j]);
        }
        resume(c);
        return false;
    }
};
DLX dlx;
int n, m;
void solve() {
    while(~scanf("%d %d", &n, &m)) {
        dlx.init(n, m);
        int c, wh;
        for(int i=0; i<n; i++) {
            scanf("%d", &c);
            for(int j=0; j<c; j++) {
                scanf("%d", &wh);
                dlx.Link(i+1, wh);
            }
        }
        if(dlx.Dance(0) == false) puts("NO");
    }
}
int main(void) {
    #ifdef DK
        freopen("/home/dk/桌面/1.in","r",stdin);
    #endif // DK
    solve();
    return 0;
}

http://acm.hust.edu.cn/vjudge/contest/view.action?cid=65998#problem/B

第二題是問你是否可以用給定的矩形將這個大的區域(矩形)填滿. 因爲數據很小, 把每一個小方格看做爲一個任務目標(列), 一個矩形對應着一行,有多少個小方格,就在小方格的位置上置1,其餘爲0, 構圖好了之後, 就直接用模板搜.

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<string>
#include<stack>
#include<queue>
#include<vector>
#include<map>
#include<set>
#include<iostream>
#define pb push_back
#define INF 0x3f3f3f3f
using namespace std;
typedef unsigned long long ULL;
typedef long long LL;
const int mod = 1000000007;
const int N = 35;
const int M = 505;
const int maxnode = N*N*N*2 + M + 50;
struct DLX {
    int n,m,size;
    int U[maxnode],D[maxnode],R[maxnode],L[maxnode],Row[maxnode],Col[maxnode];
    int H[N*N],S[N*N];
    int ansd,ans[N*N];
    void init(int _n,int _m) {
        n = _n; m = _m;
        for(int i = 0;i <= m;i++) {
            S[i] = 0;
            U[i] = D[i] = i;
            L[i] = i-1;
            R[i] = i+1;
        }
        R[m] = 0; L[0] = m;
        size = m;
        for(int i = 1;i <= n;i++)H[i] = -1;
        ansd = -1;
    }
    void Link(int r,int c) {
        ++S[Col[++size]=c];
        Row[size] = r;
        D[size] = D[c];
        U[D[c]] = size;
        U[size] = c;
        D[c] = size;
        if(H[r] < 0)H[r] = L[size] = R[size] = size;
        else {
            R[size] = R[H[r]];
            L[R[H[r]]] = size;
            L[size] = H[r];
            R[H[r]] = size;
        }
    }
    void remove(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[Col[j]];
            }
    }
    void resume(int c) {
        for(int i = U[c];i != c;i = U[i])
            for(int j = L[i];j != i;j = L[j])
                ++S[Col[U[D[j]]=D[U[j]]=j]];
        L[R[c]] = R[L[c]] = c;
    }
    void Dance(int d) {
        if(ansd != -1 && ansd <= d) return ;/// 這裏對超過最小答案的情況進行剪枝...不然會光榮超時..
        if(R[0] == 0) {
            if(ansd == -1) ansd = d;
            ansd = min(ansd, d);
            return ;
        }
        int c = R[0];
        for(int i = R[0];i != 0;i = R[i])
            if(S[i] < S[c])
                c = i;
        remove(c);
        for(int i = D[c];i != c;i = D[i]) {
            ans[d] = Row[i];
            for(int j = R[i];j != i;j = R[j])remove(Col[j]);
            Dance(d+1);
            for(int j = L[i];j != i;j = L[j])resume(Col[j]);
        }
        resume(c);
    }
};
DLX dlx;
void solve() {
    int cas, n, m, p;;
    scanf("%d", &cas);
    while(cas--) {
        scanf("%d %d %d", &n, &m, &p);
        dlx.init(p, n*m);
        for(int i=1; i<=p; i++) {
            int x1, x2, y1, y2;
            scanf("%d %d %d %d", &x1, &y1, &x2, &y2);
            for(int j=x1+1; j<=x2; j++)
                for(int k=y1+1; k<=y2; k++)
                    dlx.Link(i, (j-1)*m+k);
        }
        dlx.Dance(0);
        printf("%d\n", dlx.ansd);
    }
}
int main(void)
{
#ifdef DK
    freopen("/home/dk/桌面/1.in","r",stdin);
#endif // DK
    solve();
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章