DLX 精確覆蓋問題

2021-06-28 01:09:42 字數 1914 閱讀 7120

精確覆蓋問題: 

給定乙個由0-1組成的矩陣, 問是否能找到乙個行的集合, 使得集合中每一列都恰好包含乙個1(如圖)。

演算法x: 通過dfs, 每次選取一行可行

模擬演算法x過程:

很容易想到上面的搜尋, 但是狀態的改變很難操作, 包括了刪除和復原, 使用普通的資料結構運算根本停不下來。 於是, 一位大師想到了神奇的資料結構dancing links。

類似乙個雙向鍊錶, 對於每個節點, 記錄其左邊節點和右邊節點, 這時會發現刪除和復原的操作實現變得可能了。

在記錄左右節點的同時, 我們還要記錄上下節點和當前節點所屬的行和列, 這些變數在接下來的操作中要用到。

於是, 我們得到乙個縱橫交錯的圖。

圖中的ci是用來輔助的虛擬節點, 而所有虛擬節點的最前面有乙個頭結點head。 由於是迴圈鍊錶, 對於每一行最開始的元素, 其左節點即為該行的最右邊的節點, 同樣地, 每一行最後的元素的右節點即為最開頭的節點。 每一列同理。現在, 我們可以高效地進行刪除列和復原列的操作了, 具體過程見**。

模仿rujia liu的寫法, 將整個dlx作為乙個結構體進行編寫, 大體結構如下:

struct dlx

//初始化虛擬節點

void add(int bel, vector& a) //讀入資料後構建dancing links

void set_out(int c) //將列刪除

void set_in(int c) //將列復原

bool dfs(int x) //搜尋過程

bool solve(vector& v) //解決問題};

具體**的實現如下:

#include #include #include #include #define n 1000 + 10

using namespace std;

struct dlx

r[n] = 0, l[0] = n;

r[0] = 1;

sz = n + 1;

memset(sum, 0, sizeof sum);//初始化

}void add(int bel, vector& a)

r[sz-1] = now, l[now] = sz - 1;//將之前i = 0時和當前的錯誤賦值糾正

}void set_out(int c)

}void set_in(int c)

l[r[c]] = c;

r[l[c]] = c;

}//注意: set_in和set_out的方向相反

bool dfs(int x)

int c = r[0];

for (int i = r[0]; i != 0; i = r[i])

if (sum[i] < sum[c]) c = i;//尋找節點數最少的一列

set_out(c);

for (int i = d[c]; i != c; i = d[i])

set_in(c);

return false;

}bool solve(vector& v)

}test;

int n, m, x;

vectorv;

int main()

test.add(i, v);//在dancing links中新增一行

}v.clear();

if (test.solve(v))

else printf("no answer!\n");//呼叫solve函式

return 0;

}

DLX 精確覆蓋 重複覆蓋

給定乙個n m的矩陣,有些位置為1,有些位置為0。如果g i j 1則說明i行可以覆蓋j列。problem 1 選定最少的行,使得每列有且僅有乙個1.2 選定最少的行,使得每列至少乙個1.這類屬於np問題的問題,可以使用搜尋解決。但是普通的搜尋必超時無疑。因此我們要設法加優化來加快速度。dancin...

POJ 3740 精確覆蓋問題,DLX模版

花3小時打上的注釋,分享給大家。1 include 2 include 3 const int maxr 20 4 const int maxc 310 5 const int maxn maxr maxc maxc 6const int inf maxr 10 7 8int n,m 9int l ...

poj 3740 DLX(精確覆蓋)

題意 經典的精確覆蓋問題。思路 精確覆蓋問題是npc的,用dlx能夠比較有效的搜尋。寫完兩個數獨再寫這個就不難了 實際上這個是原始問題,數獨只是dlx的應用 include include define n 16 define m 300 struct nodep n m m 5 int s n 5...