bzoj1823_2-SAT

10 篇文章 0 订阅

练手用裸题, 用最基本的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 + 10

using 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值