只判定是否有解
例题:http://acm.hdu.edu.cn/showproblem.php?pid=3622
O(|V||E|) 方法
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <utility>
#include <cstring>
#include <map>
#include <climits>
#include <queue>
#include <cmath>
#include <set>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef unsigned UI;
const int MAXN(201);
const int MAXE(40001);
const int MAXK(100010);
const int MAXL(10);
const int MAXC(2);
const int INF((INT_MAX - 1) / 2);
const int F(0);
template<typename T>
inline bool checkmax(T &a, const T &b) {
return b > a ? ((a = b), true) : false;
}
template<typename T>
inline bool checkmin(T &a, const T &b) {
return b < a ? ((a = b), true) : false;
}
template<typename T>
inline T ABS(T a) {
return a < 0 ? -a : a;
}
double dis(double x1, double y1, double x2, double y2) {
return sqrt((y2 - y1)*(y2 - y1) + (x2 - x1)*(x2 - x1));
}
double dis2(double x1, double y1, double x2, double y2) {
return (y2 - y1)*(y2 - y1) + (x2 - x1)*(x2 - x1);
}
struct E {
int u, v;
E *next;
E(int u_, int v_, int w_, E *n_) :u(u_), v(v_), next(n_) {}
E(){}
};
double disArr[MAXN][MAXN];
double coor[MAXN][2];
int label[MAXN];
E *first[MAXN];
E edge[MAXE], *rear;
int st[MAXN], stn;
void init(int n) {
memset(first, 0, sizeof(first[0])*(n + 1));
rear = edge;
}
void addEdge(int u, int v) {
rear->u = u;
rear->v = v;
rear->next = first[u];
first[u] = rear++;
}
bool conflict(int a, int b, double m) {
return disArr[a][b] < m;
}
bool dfs(int u) {
int ind = u >> 1, fl = u & 1;
if (label[ind] == -1)
label[ind] = fl;
else
return label[ind] == fl;
st[stn++] = ind;
for (E *i = first[u]; i; i = i->next) {
if (!dfs(i->v)) return false;
}
return true;
}
bool check(double m, int n) {
init(n * 2);
for (int i = 0; i < n; ++i) label[i] = -1;
for (int i = 0; i < n; ++i)
for (int j = i + 1; j < n; ++j) {
int ti = i * 2, tj = j * 2;
for (int k1 = 0; k1 < 2; ++k1)
for (int k2 = 0; k2 < 2; ++k2) {
int a = ti | k1, b = tj | k2;
if (conflict(a, b, m)) {
addEdge(a, b ^ 1);
addEdge(b, a ^ 1);
}
}
}
for (int i = 0; i < n; ++i) {
if (label[i] != -1) continue;
stn = 0;
if (!dfs(i * 2)){
while (stn > 0) label[st[--stn]] = -1;
if (!dfs(i * 2 + 1)) return false;
}
}
return true;
}
int main() {
int n;
while (~scanf("%d", &n)) {
double low = 0, high = 0;
init(n * 2);
for (int i = 0; i < n; ++i) {
int ti = i * 2;
scanf("%lf%lf", coor[ti], coor[ti] + 1);
scanf("%lf%lf", coor[ti+1], coor[ti+1] + 1);
}
for (int i = 0; i < 2 * n; ++i)
for (int j = i + 1; j < 2 * n; ++j) {
disArr[i][j] = disArr[j][i] = dis2(coor[i][0], coor[i][1], coor[j][0], coor[j][1]);
checkmax(high, disArr[i][j]);
}
while (high - low > 1e-6) {
double m = (high + low) / 2;
if (check(m, n))
low = m;
else
high = m;
}
printf("%.2f\n", sqrt((high + low) / 2)/2);
}
return 0;
}
O(|V|+|E|) 方法,此题题数据弱,不如上一个方法快
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <utility>
#include <cstring>
#include <map>
#include <climits>
#include <queue>
#include <cmath>
#include <set>
#include <stack>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef unsigned UI;
const int MAXN(201);
const int MAXE(40001);
const int MAXK(100010);
const int MAXL(10);
const int MAXC(2);
const int INF((INT_MAX - 1) / 2);
const int F(0);
template<typename T>
inline bool checkmax(T &a, const T &b) {
return b > a ? ((a = b), true) : false;
}
template<typename T>
inline bool checkmin(T &a, const T &b) {
return b < a ? ((a = b), true) : false;
}
template<typename T>
inline T ABS(T a) {
return a < 0 ? -a : a;
}
double dis(double x1, double y1, double x2, double y2) {
return sqrt((y2 - y1)*(y2 - y1) + (x2 - x1)*(x2 - x1));
}
double dis2(double x1, double y1, double x2, double y2) {
return (y2 - y1)*(y2 - y1) + (x2 - x1)*(x2 - x1);
}
struct E {
int u, v;
E *next;
E(int u_, int v_, int w_, E *n_) :u(u_), v(v_), next(n_) {}
E(){}
};
double disArr[MAXN][MAXN];
double coor[MAXN][2];
int label[MAXN];
E *first[MAXN];
E edge[MAXE], *rear;
void init(int n) {
memset(first, 0, sizeof(first[0])*(n + 1));
rear = edge;
}
void addEdge(int u, int v) {
rear->u = u;
rear->v = v;
rear->next = first[u];
first[u] = rear++;
}
bool conflict(int a, int b, double m) {
return disArr[a][b] < m;
}
int dfn[MAXN], anc[MAXN];
int scc[MAXN];
int dfsClo, sccCnt;
stack<int> st;
void dfs(int u) {
dfn[u] = anc[u] = dfsClo++;
st.push(u);
for (E *i = first[u]; i; i = i->next) {
int v = i->v;
if (dfn[v] == -1) {
dfs(v);
checkmin(anc[u], anc[v]);
}
else {
if (scc[v] == -1) {
checkmin(anc[u], dfn[v]);
}
}
}
if (dfn[u] == anc[u]) {
int t;
do {
t = st.top();
st.pop();
scc[t] = sccCnt;
} while (t != u);
++sccCnt;
}
}
void findScc(int n) {
for (int i = 0; i < n; ++i) dfn[i] = anc[i] = scc[i] = -1;
dfsClo = sccCnt = 0;
for (int i = 0; i < n; ++i)
if (dfn[i] == -1)
dfs(i);
}
bool check(double m, int n) {
init(n * 2);
for (int i = 0; i < n; ++i) label[i] = -1;
for (int i = 0; i < n; ++i)
for (int j = i + 1; j < n; ++j) {
int ti = i * 2, tj = j * 2;
for (int k1 = 0; k1 < 2; ++k1)
for (int k2 = 0; k2 < 2; ++k2) {
int a = ti | k1, b = tj | k2;
if (conflict(a, b, m)) {
addEdge(a, b ^ 1);
addEdge(b, a ^ 1);
}
}
}
findScc(n * 2);
for (int i = 0; i < n; ++i)
if (scc[i * 2] == scc[i * 2 + 1])
return false;
return true;
}
int main() {
int n;
while (~scanf("%d", &n)) {
double low = 0, high = 0;
init(n * 2);
for (int i = 0; i < n; ++i) {
int ti = i * 2;
scanf("%lf%lf", coor[ti], coor[ti] + 1);
scanf("%lf%lf", coor[ti+1], coor[ti+1] + 1);
}
for (int i = 0; i < 2 * n; ++i)
for (int j = i + 1; j < 2 * n; ++j) {
disArr[i][j] = disArr[j][i] = dis2(coor[i][0], coor[i][1], coor[j][0], coor[j][1]);
checkmax(high, disArr[i][j]);
}
while (high - low > 1e-6) {
double m = (high + low) / 2;
if (check(m, n))
low = m;
else
high = m;
}
printf("%.2f\n", sqrt((high + low) / 2)/2);
}
return 0;
}
增加了与操作和异或操作后
例题:http://poj.org/problem?id=3678
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <utility>
#include <cstring>
#include <map>
#include <climits>
#include <queue>
#include <cmath>
#include <set>
#include <stack>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef unsigned UI;
const int MAXN(2010);
const int MAXE(4000010);
const int MAXK(100010);
const int MAXL(10);
const int MAXC(2);
const int INF((INT_MAX - 1) / 2);
const int F(0);
template<typename T>
inline bool checkmax(T &a, const T &b) {
return b > a ? ((a = b), true) : false;
}
template<typename T>
inline bool checkmin(T &a, const T &b) {
return b < a ? ((a = b), true) : false;
}
template<typename T>
inline T ABS(T a) {
return a < 0 ? -a : a;
}
double dis(double x1, double y1, double x2, double y2) {
return sqrt((y2 - y1)*(y2 - y1) + (x2 - x1)*(x2 - x1));
}
double dis2(double x1, double y1, double x2, double y2) {
return (y2 - y1)*(y2 - y1) + (x2 - x1)*(x2 - x1);
}
struct E {
int u, v;
E *next;
E(int u_, int v_, int w_, E *n_) :u(u_), v(v_), next(n_) {}
E(){}
};
E *first[MAXN];
E edge[MAXE], *rear;
void init(int n) {
memset(first, 0, sizeof(first[0])*(n + 1));
rear = edge;
}
void addEdge(int u, int v) {
rear->u = u;
rear->v = v;
rear->next = first[u];
first[u] = rear++;
}
int dfn[MAXN], anc[MAXN];
int scc[MAXN];
int dfsClo, sccCnt;
stack<int> st;
void dfs(int u) {
dfn[u] = anc[u] = dfsClo++;
st.push(u);
for (E *i = first[u]; i; i = i->next) {
int v = i->v;
if (dfn[v] == -1) {
dfs(v);
checkmin(anc[u], anc[v]);
}
else {
if (scc[v] == -1) {
checkmin(anc[u], dfn[v]);
}
}
}
if (dfn[u] == anc[u]) {
int t;
do {
t = st.top();
st.pop();
scc[t] = sccCnt;
} while (t != u);
++sccCnt;
}
}
void findScc(int n) {
for (int i = 0; i < n; ++i) dfn[i] = anc[i] = scc[i] = -1;
dfsClo = sccCnt = 0;
for (int i = 0; i < n; ++i)
if (dfn[i] == -1)
dfs(i);
}
int label[MAXN];
bool check(int n) {
findScc(n * 2);
for (int i = 0; i < n; ++i)
if (scc[i * 2] == scc[i * 2 + 1])
return false;
for (int i = 0; i < n; ++i) {
label[i] = scc[i * 2] < scc[i * 2 + 1] ? 1 : 0;
}
return true;
}
int rec[MAXN][2];
void AND(int a, int b) {
addEdge(a ^ 1, a);
addEdge(b ^ 1, b);
}
void OR(int a, int b) {
addEdge(a ^ 1, b);
addEdge(b ^ 1, a);
}
void XOR(int a, int b) {
addEdge(a ^ 1, b);
addEdge(b, a ^ 1);
addEdge(b ^ 1, a);
addEdge(a, b ^ 1);
}
void EOR(int a, int b) {
addEdge(a,b);
addEdge(a^1, b^1);
addEdge(b, a);
addEdge(b^1, a^1);
}
int main() {
int n, m;
while (~scanf("%d%d", &n, &m)) {
init(2 * n);
int a, b, c;
char op[5];
for (int i = 0; i < m; ++i) {
scanf("%d%d%d%s", &a, &b, &c, op);
switch (op[0]) {
case 'A':
if (c) AND(a*2, b*2);
else OR(a * 2 + 1, b * 2 + 1);
break;
case 'O':
if (c) OR(a * 2, b * 2);
else AND(a * 2 + 1, b * 2 + 1);
break;
case 'X':
if (c) XOR(a * 2, b * 2);
else EOR(a * 2 , b * 2 );
break;
}
}
printf("%s\n", check(n) ? "YES" : "NO");
}
return 0;
}
输出最小字典序方案
例题:http://acm.hdu.edu.cn/showproblem.php?pid=1814
只能用
O(|V||E|)
方法
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <utility>
#include <cstring>
#include <map>
#include <climits>
#include <queue>
#include <cmath>
#include <set>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef unsigned UI;
const int MAXN(16010);
const int MAXE(40010);
const int MAXK(100010);
const int MAXL(10);
const int MAXC(2);
const int INF((INT_MAX - 1) / 2);
const int F(0);
template<typename T>
inline bool checkmax(T &a, const T &b) {
return b > a ? ((a = b), true) : false;
}
template<typename T>
inline bool checkmin(T &a, const T &b) {
return b < a ? ((a = b), true) : false;
}
template<typename T>
inline T ABS(T a) {
return a < 0 ? -a : a;
}
double dis(double x1, double y1, double x2, double y2) {
return sqrt((y2 - y1)*(y2 - y1) + (x2 - x1)*(x2 - x1));
}
double dis2(double x1, double y1, double x2, double y2) {
return (y2 - y1)*(y2 - y1) + (x2 - x1)*(x2 - x1);
}
struct E {
int u, v;
E *next;
E(int u_, int v_, int w_, E *n_) :u(u_), v(v_), next(n_) {}
E() {}
};
int label[MAXN];
E *first[MAXN];
E edge[MAXE], *rear;
int st[MAXN], stn;
void init(int n) {
memset(first, 0, sizeof(first[0])*(n + 1));
rear = edge;
}
void addEdge(int u, int v) {
rear->u = u;
rear->v = v;
rear->next = first[u];
first[u] = rear++;
}
bool dfs(int u) {
int ind = u >> 1, fl = u & 1;
if (label[ind] == -1)
label[ind] = fl;
else
return label[ind] == fl;
st[stn++] = ind;
for (E *i = first[u]; i; i = i->next) {
if (!dfs(i->v)) return false;
}
return true;
}
bool check(int n) {
for (int i = 0; i < n; ++i) label[i] = -1;
for (int i = 0; i < n; ++i) {
if (label[i] != -1) continue;
stn = 0;
if (!dfs(i * 2)) {
while (stn > 0) label[st[--stn]] = -1;
if (!dfs(i * 2 + 1)) return false;
}
}
return true;
}
int main() {
int n, m;
while (~scanf("%d%d", &n, &m)) {
init(2 * n);
int a, b;
for (int i = 0; i < m; ++i) {
scanf("%d%d", &a, &b);
--a;
--b;
addEdge(a, b ^ 1);
addEdge(b, a ^ 1);
}
if (check(n)) {
for (int i = 0; i < n; ++i)
printf("%d\n", (2 * i | label[i]) + 1);
}
else
printf("NIE\n");
}
return 0;
}
输出任意一组方案
例题:http://poj.org/problem?id=3648
需注意的是此题要输出合法赋值取反后的结果
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <utility>
#include <cstring>
#include <map>
#include <climits>
#include <queue>
#include <cmath>
#include <set>
#include <stack>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef unsigned UI;
const int MAXN(5000);
const int MAXE(10010);
const int MAXK(100010);
const int MAXL(10);
const int MAXC(2);
const int INF((INT_MAX - 1) / 2);
const int F(0);
template<typename T>
inline bool checkmax(T &a, const T &b) {
return b > a ? ((a = b), true) : false;
}
template<typename T>
inline bool checkmin(T &a, const T &b) {
return b < a ? ((a = b), true) : false;
}
template<typename T>
inline T ABS(T a) {
return a < 0 ? -a : a;
}
double dis(double x1, double y1, double x2, double y2) {
return sqrt((y2 - y1)*(y2 - y1) + (x2 - x1)*(x2 - x1));
}
double dis2(double x1, double y1, double x2, double y2) {
return (y2 - y1)*(y2 - y1) + (x2 - x1)*(x2 - x1);
}
struct E {
int u, v;
E *next;
E(int u_, int v_, int w_, E *n_) :u(u_), v(v_), next(n_) {}
E(){}
};
E *first[MAXN];
E edge[MAXE], *rear;
void init(int n) {
memset(first, 0, sizeof(first[0])*(n + 1));
rear = edge;
}
void addEdge(int u, int v) {
rear->u = u;
rear->v = v;
rear->next = first[u];
first[u] = rear++;
}
int dfn[MAXN], anc[MAXN];
int scc[MAXN];
int dfsClo, sccCnt;
stack<int> st;
void dfs(int u) {
dfn[u] = anc[u] = dfsClo++;
st.push(u);
for (E *i = first[u]; i; i = i->next) {
int v = i->v;
if (dfn[v] == -1) {
dfs(v);
checkmin(anc[u], anc[v]);
}
else {
if (scc[v] == -1) {
checkmin(anc[u], dfn[v]);
}
}
}
if (dfn[u] == anc[u]) {
int t;
do {
t = st.top();
st.pop();
scc[t] = sccCnt;
} while (t != u);
++sccCnt;
}
}
void findScc(int n) {
for (int i = 0; i < n; ++i) dfn[i] = anc[i] = scc[i] = -1;
dfsClo = sccCnt = 0;
for (int i = 0; i < n; ++i)
if (dfn[i] == -1)
dfs(i);
}
int label[MAXN];
bool check(int n) {
findScc(n * 2);
for (int i = 0; i < n; ++i)
if (scc[i * 2] == scc[i * 2 + 1])
return false;
for (int i = 0; i < n; ++i) {
label[i] = scc[i * 2] < scc[i * 2 + 1] ? 0 : 1;
}
return true;
}
int main() {
int n, m;
while (~scanf("%d%d", &n, &m)) {
init(2 * n);
int a, b;
char ca, cb;
for (int i = 0; i < m; ++i) {
scanf("%d %c %d %c", &a, &ca, &b, &cb);
int ta = 2*a + (ca == 'h' ? 0 : 1);
int tb = 2*b + (cb == 'h' ? 0 : 1);
addEdge(ta, tb ^ 1);
addEdge(tb, ta ^ 1);
}
addEdge(1, 0);
if (check(n)) {
bool pr(false);
for (int i = 1; i < n; ++i) {
if (pr) printf(" ");
else
pr = true;
printf("%d", i);
printf("%c", ((label[i]^1) == 0 ? 'h' : 'w'));
}
printf("\n");
}
else
printf("bad luck\n");
}
return 0;
}
算法的正确性简要分析
这里对赵爽在《2-SAT解法浅析》中的算法给出宏观的证明思路。
如果将
x
和
1. sat条件;
2.
x
和
对于染色算法的正确性来说证明的思路大致如下:
假设染红色表示该点取1,染蓝色表示该点取0。首先由于我们的建图方法,只要保证不存在这样的
u→v
,使得
u
染红色,
首先证明了存在一种染色方案,其满足条件2。
进一步证明该染色方案满足条件1。
就完成了整个证明。
添加了与操作和异(同)或操作后,可以仿照类似的思路,证明原算法仍然适用。
进一步根据对称性我们还可以证明,在原图中,按照拓扑逆序染色,被染蓝色的点,其祖先也早被染成蓝色,因此就得到了不用反向建图求具体方案的算法(对加入了与操作和异(同)或操作后的情况也成立)。