精確覆蓋問題:
給定乙個由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...