1.关于DLX的重复覆盖:根据与精确覆盖概念的区别可知,只需改变remove()和resume()函数控制删除和恢复的过程即可实现,对于求解最少步数问题,可借助ida*中的h()函数优化。
2.重复覆盖+精确覆盖:某些元素可重复覆盖(目标),而某些元素只能精确覆盖(每类元素只能使用一次),这是要对前m列进行重复覆盖的删除回复操作,对后面的列进行精确覆盖的操作,也有一些特殊情况可直接使用重复覆盖代替。如hdu2828,每类元素只有两种,可通过visit数组在选中某个元素时删除同类中的另一个元素,实现精确覆盖)。
3.K-sat问题:
考虑CNF
Φ=C1∧C2∧...∧Ci∧...Cn
子句Ci具有如下形式
Xij为命题变元集Xij为{x1,x2....xm}中的一个变元,其中Xij称为正文字,Xij称为负文字,子句Ci中文字的个数定义为子句的长度,用li表示。对于一个子句Ci,只要其中一个文字为真,则称该子句是可满足的。
一个判定形式的SAT问题是指:对于给定的CNF是否存在一组关于命题的变元的真值赋值使得为真。
特殊的,当每个子句的长度为K时,则成为K-SAT问题。
以3-SAT为例(xmu1101),有遗传算法和dlx两种做法,这里介绍后者。
问题:有n个元素,每个元素有选和不选两种状态,因此可以看做n*2中元素;m个约束,每个约束要求某3个元素中至少选一个。目标:能否从2*n个元素中选择一些元素使其满足所有约束。
建模:由于要是元素m种约束,因此把m种约束看做重复覆盖的列,把2*n中元素看做行,如果第i个元素在第j个约束中,则mat[i][j]=1,同时每个元素只能有一种状态,因此将n个元素作为精确覆盖的列mat[i][m+i]=mat[i+n][m+i]=1,求解是否存在满足条件的覆盖即可,输出方案也很简单。
import java.util.Arrays; import java.util.Scanner; public class SAT_3 { class DLX { int maxn = 1010, inf = 1 << 28; int L[] = new int[maxn], R[] = new int[maxn], D[] = new int[maxn], U[] = new int[maxn]; int Row[] = new int[maxn], C[] = new int[maxn], S[] = new int[maxn]; // 元素x所在行列 每列元素个数 int m, id, rowid; void init(int m) { this.m = m; for (int i = 0; i <= m; i++) { D[i] = U[i] = i; S[i] = 0; L[i] = i - 1; R[i] = i + 1; } L[0] = m; R[m] = 0; id = m + 1; rowid = 1; } void insert(int arr[], int len) { for (int i = 0; i < len; i++, id++) { int x = arr[i]; C[id] = x; Row[id] = rowid; S[x]++; D[id] = x; U[id] = U[x]; D[U[x]] = id; U[x] = id; if (i == 0) L[id] = R[id] = id; else { L[id] = id - 1; R[id] = id - i; L[id - i] = id; R[id - 1] = id; } } rowid++; } void remove(int c) { for (int i = U[c]; i != c; i = U[i]) { L[R[i]] = L[i]; R[L[i]] = R[i]; } } void resume(int c) { for (int i = D[c]; i != c; i = D[i]) { L[R[i]] = i; R[L[i]] = i; } } void ExRemove(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]) { S[C[j]]--; U[D[j]] = U[j]; D[U[j]] = D[j]; } } void ExResume(int c) { for (int i = U[c]; i != c; i = U[i]) for (int j = L[i]; j != i; j = L[j]) { S[C[j]]++; U[D[j]] = j; D[U[j]] = j; } L[R[c]] = c; R[L[c]] = c; } boolean dance() { if (R[0] == 0 || R[0] > bound) return true; int c = R[0]; for (int i = R[0]; i != 0; i = R[i]) if (S[i] < S[c] && i <= bound) c = i; for (int i = D[c]; i != c; i = D[i]) { remove(i); int idx = -1; for (int j = R[i]; j != i; j = R[j]) { if (C[j] <= bound) remove(j); else idx = j; } if (idx != -1) ExRemove(C[idx]); // C[]!! if (dance()) return true; if (idx != -1) ExResume(C[idx]); // C[]!! for (int j = L[i]; j != i; j = L[j]) if (C[j] <= bound) resume(j); resume(i); } return false; } int bound; void solve(int b) { this.bound = b; if (dance()) System.out.println("Yes"); else System.out.println("No"); } } DLX dlx=new DLX(); Scanner scan=new Scanner(System.in); int cnf[][]=new int[110][210],len[]=new int[110]; void run() { int n=scan.nextInt(); int m=scan.nextInt(); for(int i=1;i<=n;i++){ cnf[i][0]=cnf[i+n][0]=m+i; len[i]=len[i+n]=1; } dlx.init(m+n); for(int i=1;i<=m;i++){ for(int j=0;j<3;j++){ int v=scan.nextInt(); if(v<0) v=n-v; cnf[v][len[v]++]=i; } } for(int i=1;i<=n*2;i++) dlx.insert(cnf[i], len[i]); dlx.solve(m); } public static void main(String[] args) { new SAT_3().run(); } }