hihoCoder #1467 : 2-SAT·hihoCoder音乐节

题目链接

描述

hihoCoder音乐节由hihoCoder赞助商大力主办,邀请了众多嘉宾和知名乐队参与演出。

音乐会分为上午、下午两场进行,主办方指定了n首歌让乐队进行演唱。每首歌只会被演唱一次,要么在上午要么在下午。

参加音乐会的嘉宾们对于歌曲的演唱时间有一些要求。具体来说,每位嘉宾会指定两首歌曲的演唱时间(上午或者下午)。如果最后实际的演出安排中,两首歌都没有达到嘉宾的要求,那么嘉宾就会对音乐节不滿意。如嘉宾A的要求是上午《我的滑板鞋》和下午《忐忑》,而最后的演出中上午没有《我的滑板鞋》只有《忐忑》,下午没有《忐忑》只有《我的滑板鞋》,那么嘉宾A是不满意的。

音乐节主办方自然希望使所有嘉宾满意,但主办方后来发现有可能不存在一种歌曲的安排方案满足所有嘉宾,所以他们希望你判断一下这种情况是否会发生。

输入

输入第一行包含一个数字 K,代表K组数据。(K≤50)

对于每一组数据,第一行包含两个非负整数n和m(n≤100,m≤1000),代表有n首歌和m位嘉宾。

为了方便我们给予歌编号,编号分别从1 到n。接下的m行,每行都代表对应的嘉宾的喜好由一个英文字母(m或h)跟一个数字代表,如m1 代表这个评审喜欢第1首歌上午进行,而h2代表这个评审员喜欢第2首歌下午进行。

输出

对于每一组数据,输出一行,如果能满足所有嘉宾的情况,输出GOOD;否则输出BAD。

样例输入

2
3 4
m3 h1
m1 m2
h1 h3
h3 m2
2 4
h1 m2
m2 m1
h1 h2
m1 h2

样例输出

GOOD
BAD

思路

2-SAT问题:一共有 N N N个选项 M M M个要求,求一种满足所有约束条件的方案

  • 首先把 N N N个选项拆成 2 N 2N 2N个分别表示相反的状态
  • 然后根据约束条件建图,例如 ( a (a (a or b ) b) b) 我们就建这样两个边: ¬ a ¬a ¬a–> b b b ¬ b ¬b ¬b–> a a a 表示当 a a a不成立时 b b b一定要成立 或者 b b b不成立 a a a一定要成立,这样就能保证至少有一个条件满足,而且能能够尽可能的满足多个约束条件
  • 矛盾情况就是,最后求得的一个联通分量里面包含有一个点的两个状态(这个状态既要满足又要不满足)就是矛盾

我把输入的数据按照字符串来处理刚开始一直WA,我在判断数字的时候只截取了S[1],编号可是100啊!

#include <bits/stdc++.h>
#define LL long long
#define P pair<int, int>
#include <time.h>
#define lowbit(x) (x & -x)
#define mem(a, b) memset(a, b, sizeof(a))
#define rep(i, a, n) for (int i = a; i <= n; ++i)
const int maxn = 305;
#define mid ((l + r) >> 1)
#define lc rt<<1
#define rc rt<<1|1
using namespace std;
int tot, len, ts;
int low[maxn], dfn[maxn], Stack[maxn], inStack[maxn], belong[maxn]; 
vector<int> g[maxn];

void tarjan(int x) {
    Stack[len++] = x;
    inStack[x] = 1;
    low[x] = dfn[x] = ++ts;
    for (int i = 0; i < (int)g[x].size(); ++i) {
        int y = g[x][i];
        if (!dfn[y]) tarjan(y), low[x] = min(low[x], low[y]);
        else if (inStack[y]) low[x] = min(low[x], low[y]);
    }
    if (dfn[x] == low[x]) {
        tot++;
        int top;
        do{
            top = Stack[--len];
            inStack[top] = 0;
            belong[top] = tot;
        }while (top != x);
    }
}


int main() {
#ifndef ONLINE_JUDGE
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    
    map<char, int> mi;
    mi['m'] = 0;

    int T;
    scanf("%d", &T);
    int n, m;
    while (T--) {
        scanf("%d %d", &n, &m);
        len = ts = tot = 0;
        mem(inStack, 0);
        mem(low, 0);
        mem(Stack, 0);
        mem(belong, 0);
        mem(dfn, 0);
        for (int i = 0; i < maxn; ++i) g[i].clear();
        mi['h'] = n; 
        for (int i = 0; i < m; ++i) {
            int l, r, l1, r1; // l,r 表示一个约束条件 l1,r1 分表表示反状态
            char x, y;
            scanf(" %c%d %c%d ", &x, &l, &y, &r);
            l += mi[x];
            r += mi[y];
            l1 = l > n ? l-n : l+n;
            r1 = r > n ? r-n : r+n;
            // 建图
            g[l1].push_back(r);
            g[r1].push_back(l);
        }   
        // tarjan 
        for (int i = 1; i<= n*2; ++i) {
            if (!dfn[i]) tarjan(i);
        }
        int flag = 0;
        for (int i = 1; i <= n; ++i) {
            if (belong[i] == belong[i+n]) flag = 1;
        }
        puts(flag ? "BAD" : "GOOD");
    }
    
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值