\(\text\),舞蹈鏈,主要解決精確覆蓋問題和重複覆蓋問題。
本文暫時只介紹精確覆蓋問題。
重複覆蓋問題不知什麼時候再更。
p4929 【模板】舞蹈鏈(dlx)
題意:給定乙個 \(n\) 行 \(m\) 列的矩陣,矩陣中每個元素要麼是1,要麼是0
你需要在矩陣中挑選出若干行,使得對於矩陣的每一列 \(j\) ,在你挑選的這些行中,有且僅有一行的第 \(j\) 個元素為 \(1\)。
大致思路
大致將這一類問題總結成了幾個步驟。
建模,將問題轉換成如上題的模板。
開始寫板子。
而板子大致又分成如下幾個步驟:
將用十字鍊錶的圖建出來。
選中一行,將其中有一的每一列標記出來。
再將標記出的每一列中有一的那一行標記出來。
將所有標記的刪掉,合成新矩陣。
若新矩陣不為空,則重複上述步驟;若為空,判斷之前是否全為一,若是,則有解,否則無解。
現在應該比較清晰了吧。
#include using namespace std;
const int maxn = 6000;
int n , m , tot , cnt;
int l[maxn] , r[maxn] , d[maxn] , u[maxn];
int qx[maxn] , qy[maxn] , vy[maxn] , ans[maxn];
inline int read()
inline void init()
r[m] = 0 , l[0] = m;
cnt = m + 1;
}inline void add(int &ll , int &rr , int x , int y)
inline void remove(int p)
return;
}inline void resume(int p)
l[r[p]] = r[l[p]] = p;
return;
}inline bool dfs()
resume(p);
return false;
}int main()
}if(dfs())
for(int i = 1;i <= tot;i++) cout << ans[i] << " ";
else
cout << "no solution!";
return 0;
}
生為乙個懶人,這就是最後一題了。
sp13980 sudogob - sudoku goblin
一道顯然的 \(\text\) 模板題。
這裡注重講一下如何建模。
首先考慮乙個數獨,需要滿足的條件。
注意最後乙個情況,這是最容易遺漏的乙個情況。
根據 \(\text\) 性質,我們不妨將每一種情況設為每一行,而每乙個限制設為每一列,就建出了模型。
考慮每一種情況
我們讓 \(a_\) 為第 \(i\) 行,第 \(j\) 列上的數。
那麼這個位置九個數都有可能被填,那麼就都需要加上去。
那麼這個位置就只有唯一的解法。
考慮每一種限制
對於之前的情況,我們需要在不同的列中,根據限制加點。
由於有四個限制,我們可以加四個點。
**大致如下:
add(((i - 1) * 9 + j - 1) * 9 + k , 81 * 0 + (i - 1) * 9 + j);
add(((i - 1) * 9 + j - 1) * 9 + k , 81 * 1 + (i - 1) * 9 + k);
add(((i - 1) * 9 + j - 1) * 9 + k , 81 * 2 + (j - 1) * 9 + k);
add(((i - 1) * 9 + j - 1) * 9 + k , 81 * 3 + ((i - 1) / 3 * 3 + (j - 1) / 3) * 9 + k);
其餘的就可以直接寫板子了。
#include using namespace std;
const int maxn = 5000;
int n , m , t , sum , cnt , tot , a[15][15];
int l[maxn] , r[maxn] , u[maxn] , d[maxn] , h[maxn] , qx[maxn] , qy[maxn] , vy[maxn] , ans[maxn];
inline int read()
inline void init()
tot = 0 , cnt = m + 1 , l[0] = m , r[m] = 0;
memset(vy , 0 , sizeof(vy));
memset(qx , 0 , sizeof(qx));
memset(qy , 0 , sizeof(qy));
memset(ans , 0 , sizeof(ans));
}inline void add(int x , int y)
inline void remove(int p)
return;
}inline void resume(int p)
l[r[p]] = r[l[p]] = p;
return;
}inline void dfs()
int p = r[0];
for(int i = r[0];i;i = r[i])
if(vy[p] > vy[i]) p = i;
remove(p);
for(int i = d[p];i != p;i = d[i])
resume(p);
}int main()}}
}dfs();
cout << sum << endl;
if(sum == 1)}}
return 0;
}
DLX 精確覆蓋問題
精確覆蓋問題 給定乙個由0 1組成的矩陣,問是否能找到乙個行的集合,使得集合中每一列都恰好包含乙個1 如圖 演算法x 通過dfs,每次選取一行可行 模擬演算法x過程 很容易想到上面的搜尋,但是狀態的改變很難操作,包括了刪除和復原,使用普通的資料結構運算根本停不下來。於是,一位大師想到了神奇的資料結構...
DLX演算法合集 I
dlx是一種相當神奇的資料結構,通常用於解決矩陣 多為稀疏矩陣 的 重複 精確 覆蓋的問題。不過一般這類問題的難點是抽出轉化關係,剩下的幾乎就是套模板 include include include include include include includeusing namespace std...
DLX 精確覆蓋 重複覆蓋
給定乙個n m的矩陣,有些位置為1,有些位置為0。如果g i j 1則說明i行可以覆蓋j列。problem 1 選定最少的行,使得每列有且僅有乙個1.2 選定最少的行,使得每列至少乙個1.這類屬於np問題的問題,可以使用搜尋解決。但是普通的搜尋必超時無疑。因此我們要設法加優化來加快速度。dancin...