2011-08-09 08:32:39Morris

a207. 精準覆蓋問題(Exact cover)

a207. 精準覆蓋問題(Exact cover)

內容 :

給定一個01矩陣, 現在選擇一些行, 使得每一列有且只有一個 1,
例子如下 :

這裡是 6 行 7 列 的矩陣, 我們取行集合{1, 4, 5}, 便可使每一列有且僅有一個 1

輸入說明 :

有多組測資,

每組第一行有兩個整數 n, m, 代表有 n 行, m 列 (1 ≦ n, m ≦ 1000)

接下來有 n 行,

每行的第一個數字 C (1 ≦ C  ≦ 100), 代表有幾個 1 在該行,

接下來有 C 個整數, 代表該列的元素值為 1, 且已排序好

輸出說明 :

輸出取的最少個數, 如果為 0, 請輸出 "No".

範例輸入 :

6 7
3 3 5 6
3 1 4 7
3 2 3 6
2 1 4
2 2 7
3 4 5 7

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

範例輸出 :

3
3

提示 :

× 測資難度偏易, 希望大家能夠通過

× 極難測資已經拔除, 瘋狂剪枝 DFS 可通過

出處 :

exact cover problem | DFS | Dancing Links (管理:morris1028)



作法 : Dancing Links

/**********************************************************************************/
/*  Problem: a207 "精準覆蓋問題(Exact cover)" from exact cover problem | DFS | Dancing Links*/
/*  Language: C                                                                   */
/*  Result: AC (224ms, 620KB) on ZeroJudge                                        */
/*  Author: morris1028 at 2011-08-06 22:52:32                                     */
/**********************************************************************************/


#include<stdio.h>
#include<stdlib.h>
#define Maxv 100000
struct DacingLinks {
    int left, right;
    int up, down;
    int data, ch, rh;
}DL[100001 + 1001];
int s[1001], o[1001], head, len, size;
void Remove(int c) {
    DL[DL[c].right].left = DL[c].left;
    DL[DL[c].left].right = DL[c].right;
    int i, j;
    for(i = DL[c].down; i != c; i = DL[i].down) {
        for(j = DL[i].right; j != i; j = DL[j].right) {
            DL[DL[j].down].up = DL[j].up;
            DL[DL[j].up].down = DL[j].down;
            s[DL[j].ch]--;
        }
    }
}
void Resume(int c) {
    int i, j;
    for(i = DL[c].down; i != c; i = DL[i].down) {
        for(j = DL[i].left; j != i; j = DL[j].left) {
            DL[DL[j].down].up = j;
            DL[DL[j].up].down = j;
            s[DL[j].ch]++;
        }
    }
    DL[DL[c].right].left = c;
    DL[DL[c].left].right = c;
}
void DFS(int k) {
    if(k > len) return;
    if(DL[head].right == head) {
        if(k < len) len = k;
        return;
    }
    int t = Maxv, c, i, j;
    for(i = DL[head].right; i != head; i = DL[i].right) {
        if(s[i] < t) {
            t = s[i], c = i;
        }
    }
    Remove(c);
    for(i = DL[c].down; i != c; i = DL[i].down) {
        o[k] = i;
        for(j = DL[i].right; j != i; j = DL[j].right)
            Remove(DL[j].ch);
        DFS(k+1);
        for(j = DL[i].left; j != i; j = DL[j].left)
            Resume(DL[j].ch);
    }
    Resume(c);
}
int new_node(int up, int down, int left, int right) {
    DL[size].up = up, DL[size].down = down;
    DL[size].left = left, DL[size].right = right;
    DL[up].down = DL[down].up = DL[left].right = DL[right].left = size;
    return size++;
}
void init(int m) {
    size = 0;
    head = new_node(0, 0, 0, 0);
    int i;
    for(i = 1; i <= m; i++) {
        new_node(i, i, DL[head].left, head);
        DL[i].ch = i, s[i] = 0;
    }
}
main() {
    int n, m, a, b, x, r, k, C = 0;
    while(scanf("%d %d", &n, &m) != EOF) {
        init(m);
        for(a = 1; a <= n; a++) {
            scanf("%d", &x);
            int row = -1;
            while(x--) {
                scanf("%d", &r);
                DL[size].ch = r, s[r]++;
                if(row == -1) {
                    row = new_node(DL[DL[r].ch].up, DL[r].ch, size, size);
                    DL[row].rh = a;
                }else {
                    k = new_node(DL[DL[r].ch].up, DL[r].ch, DL[row].left, row);
                    DL[k].rh = a;
                }
            }
        }
        len = Maxv,    DFS(0);
        if(len != Maxv)    printf("%d\n", len);
        else    puts("No");
    }
    return 0;
}