SPOJ SOPARADE(JZOJ 4696 第四次忍者大战) 根据条件构图跑2-SAT

来源:互联网 发布:用php写一个 编辑:程序博客网 时间:2024/06/06 07:46

题目大意

现在有N个位置,每个位置要填入一个14的整数,要求亮亮间的差要大于等于二。并且现在有M条约束,每条约束规定了k个位置B1,B2...Bk上的数两两不同。问是否有合法的填法。

N,M105

解题思路

我们一步步从题目的约束中挖掘性质。

首先,相邻的两个数的差的大于等于2,那么1的傍边只能是3,4。2的旁边只能是4。3的旁边只能是1。4的旁边只能是1,2。我们发现1,2和3,4的填入肯定是交错的,就是说奇数位一定是1,2,偶数位一定是3,4(反过来也行,没什么区别)。那么对于一个点就只有两个选择,那么这就非常像2-SAT了。

那么我们就按2-SAT的方式连边,比如奇数位的2就一定要像右边的偶数位的4连一条边,因为选了2就只能在旁边选4。类似的我们就解决了相邻的性质。对于M条约数也很简单,我们同样要分奇数位和偶数位讨论,因为他们之间不可能相同。那么我们只需在奇偶性相同的位置连一条交叉的双向边(比如3连到4,4连到3)。最后跑一次2-SAT判是否有解就可以了。

提示:解决相邻的约束时,只用从前面往后面连边。因为我们做2-SAT时是默认了做完了前i个位置,如果再对前面造成约束就会有问题。

程序

十分丑的代码。。。

//YxuanwKeith#include <cstring>#include <cstdio>#include <algorithm>using namespace std;const int MAXN = 2e5 + 5;int N, M, c, Test, S[MAXN];int tot, Last[MAXN], Next[MAXN * 40], Go[MAXN * 40];bool mark[MAXN];void Link(int a, int b) {    Next[++ tot] = Last[a], Last[a] = tot, Go[tot] = b;}void Read(int &Now) {    char ch = getchar();    while (ch < '0' || ch > '9') ch = getchar();    Now = 0;    while (ch >= '0' && ch <= '9') Now = Now * 10 + ch - '0', ch = getchar();}bool Dfs(int x) {    int y = (x & 1) ? (x + 1) : (x - 1);    if (mark[y]) return 0;    if (mark[x]) return 1;    mark[x] = 1;    S[c ++] = x;    for (int p = Last[x]; p; p = Next[p])        if (!Dfs(Go[p])) return 0;    return 1;}bool Solve() {    memset(mark, 0, sizeof mark);    memset(Last, 0, sizeof Last);    tot = 0;    Read(N), Read(M);    for (int i = 1; i < N; i ++) {        int a = i, b = i + 1;        Link(a * 2, b * 2 - 1);    }    for (int i = 1; i <= M; i ++) {        int Num;        Read(Num);        if (Num > 4) return 0;        static int _0[3], _1[3];        _0[0] = _1[0] = 0;        for (int j = 0; j < Num; j ++) {            int Now;            Read(Now);            if (Now & 1) _0[++ _0[0]] = Now; else _1[++ _1[0]] = Now;        }        if (_0[0] > 2 || _1[0] > 2) return 0;        if (_0[0] > 1) {            Link(_0[1] * 2, _0[2] * 2 - 1);            Link(_0[1] * 2 - 1, _0[2] * 2);            Link(_0[2] * 2, _0[1] * 2 - 1);            Link(_0[2] * 2 - 1, _0[1] * 2);        };        if (_1[0] > 1) {            Link(_1[1] * 2, _1[2] * 2 - 1);            Link(_1[1] * 2 - 1, _1[2] * 2);            Link(_1[2] * 2, _1[1] * 2 - 1);            Link(_1[2] * 2 - 1, _1[1] * 2);        }    }    for (int i = 1; i <= N * 2; i += 2) {        if (!mark[i] && !mark[i + 1]) {            c = 0;            if (!Dfs(i)) {                while (c > 0) mark[S[-- c]] = 0;                if (!Dfs(i + 1)) return 0;            }        }    }    return 1;}int main() {    int Test;    scanf("%d", &Test);    for (int i = 1; i <= Test; i ++) {        if (Solve()) printf("approved\n"); else            printf("rejected\n");    }}
2 0
原创粉丝点击