DLX解决3-SAT问题

来源:互联网 发布:淘宝微淘怎么晒买家秀 编辑:程序博客网 时间:2024/06/14 09:51

1.关于DLX的重复覆盖:根据与精确覆盖概念的区别可知,只需改变remove()和resume()函数控制删除和恢复的过程即可实现,对于求解最少步数问题,可借助ida*中的h()函数优化。

2.重复覆盖+精确覆盖:某些元素可重复覆盖(目标),而某些元素只能精确覆盖(每类元素只能使用一次),这是要对前m列进行重复覆盖的删除回复操作,对后面的列进行精确覆盖的操作,也有一些特殊情况可直接使用重复覆盖代替。如hdu2828,每类元素只有两种,可通过visit数组在选中某个元素时删除同类中的另一个元素,实现精确覆盖)。

3.K-sat问题:

考虑CNF

Φ=C1∧C2∧...∧Ci∧...Cn


子句Ci具有如下形式

Xi1Xi2∨...∨Xij∨...∨Xili


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);elseidx = 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");elseSystem.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();}}



原创粉丝点击