bzoj1823_2-SAT

来源:互联网 发布:activiti实战源码 编辑:程序博客网 时间:2024/05/05 10:26

练手用裸题, 用最基本的2-SAT算法即可。 题目要求的是对于任意一组要求至少满足其一, 则不妨设要求的事件分别为xi、xj, 则连一条有向边2i+1->2j, 这里2i+1表示xi为假, 那么如果要满足要求则xj必为真, 同理, 再连一条2j+1->2i, 图的构造完成。

基本思路只要考虑每个没有被赋值的变量就行了, 比如, 点xi未赋值时, 将其赋为真, 并沿从这里发出的边向下搜索同时赋为真, 当出现一个点xj中2j和2j+1均为真时, 说明之前的xi为真不成立, 则重新赋为假。 若当前考虑变量无论为真还是为假都矛盾, 那么该问题无解, 输出"BAD"。

上述方法的具体复杂度我不太清楚, 不过上限应该是O(n²), 用tarjan做的话应该也是可以的, 对于大数据可能更优, 这里不给出tarjan代码。

#include <cstdio>#include <cstring>#include <algorithm>#define N 1000 + 10#define M 10000 + 10using namespace std;struct edge{    int to, next;}e[M];int n, m, num, top, p[N], st[N], flag[N];int read(){    char c = getchar();    int x = 0;    while(c < '0' || c > '9') c = getchar();    while(c >= '0' && c <= '9')    {        (x *= 10) += c - '0';        c = getchar();    }    return x;}int exread(){    char c = getchar();    while(1)    {        if (c == 'm') return 1;        else if (c == 'h') return 0;        c = getchar();    }}void add(int x, int y){    e[++num].to = y;    e[num].next = p[x];    p[x] = num;}void init(){    int x, y;    top = num = 0;    memset(p, 0, sizeof p);    memset(flag, false, sizeof flag);    n = 2 * read(), m = read();    for (int i = 1; i <= m; ++i)    {        x = exread() + 2 * read();        y = exread() + 2 * read();//瞬间发现了快速读入的神奇用法        add(x ^ 1, y);        add(y ^ 1, x);    }}bool dfs(int x){    if (flag[x^1]) return false;    if (flag[x]) return true;    flag[x] = true;    st[++top] = x;    for (int i = p[x]; i; i = e[i].next)    if (!dfs(e[i].to)) return false;    return true;}void deal(){    for (int i = 1; i <= n; ++i)    if (!flag[i*2+1] && !flag[i*2])    {        top = 0;        if (!dfs(i*2))        {            while(top > 0) flag[st[top--]] = false;            if (!dfs(i*2+1))            {                printf("BAD\n");                return;            }        }    }    printf("GOOD\n");}int main(){    int t = read();    while(t--)    {        init();        deal();    }    return 0;}


0 0
原创粉丝点击