poj 3678 Katu Puzzle && POJ 3207 Ikki's Story IV - Panda's Trick(2-sat水题)

36 篇文章 4 订阅

Katu Puzzle
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 10545 Accepted: 3912

Description

Katu Puzzle is presented as a directed graph G(VE) with each edge e(a, b) labeled by a boolean operator op (one of AND, OR, XOR) and an integer c (0 ≤ c ≤ 1). One Katu is solvable if one can find each vertex Vi a value Xi (0 ≤ X≤ 1) such that for each edge e(a, b) labeled by op and c, the following formula holds:

 Xa op Xb = c

The calculating rules are:

AND01
000
101
OR01
001
111
XOR01
001
110

Given a Katu Puzzle, your task is to determine whether it is solvable.

Input

The first line contains two integers N (1 ≤ N ≤ 1000) and M,(0 ≤ M ≤ 1,000,000) indicating the number of vertices and edges.
The following M lines contain three integers (0 ≤ a < N), b(0 ≤ b < N), c and an operator op each, describing the edges.

Output

Output a line containing "YES" or "NO".

Sample Input

4 4
0 1 1 AND
1 2 1 OR
3 2 0 AND
3 0 0 XOR

Sample Output

YES

Hint

X 0 = 1,  X 1 = 1,  X 2 = 0,  X 3 = 1.

Source


题意:给你一些边,每条边有一个值和一个运算符XOR OR  AND求是否存在一些点使得所有的边根据这些运算符 
可以符合条件的权值. 

思路:

由于在2-SAT问题中,最多只对两个元素进行限制,所以可能的限制关系共有11种:

A[x]

NOT A[x]

A[x] AND A[y]

A[x] AND NOT A[y]

A[x] OR A[y]

A[x] OR NOT A[y]

NOT (A[x] AND A[y])

NOT (A[x] OR A[y])

A[x] XOR A[y]

NOT (A[x] XOR A[y])

A[x] XOR NOT A[y]

进一步,A[x] AND A[y]相当于(A[x])AND (A[y])(也就是可以拆分成A[x]与A[y]两个限制关系),NOT(A[x] OR A[y])相当于NOT A[x] AND NOT A[y](也就是可以拆分成NOT A[x]与NOT A[y]两个限制关系)。因此,可能的限制关系最多只有9种。

 

在实际问题中,2-SAT问题在大多数时候表现成以下形式:有N对物品,每对物品中必须选取一个,也只能选取一个,并且它们之间存在某些限制关系(如某两个物品不能都选,某两个物品不能都不选,某两个物品必须且只能选一个,某个物品必选)等,这时,可以将每对物品当成一个布尔值(选取第一个物品相当于0,选取第二个相当于1),如果所有的限制关系最多只对两个物品进行限制,则它们都可以转化成9种基本限制关系,从而转化为2-SAT模型。

1)如果给出A和B的限制关系,A和B必须一起选,(A and B)||(!A and !B )==true 那么选A必须选B,建边<i,j>和<j,i>还有<i',j'>和<j',i'>

         2)如果给出A和B的限制关系,选A不能选B,那么(A && !B)||(!A && B)==true,建边<i,j'>和<j,i'>

         3)如果必须选A,那么A==true,建边<i',i>

         4)如果A一定不能选,那么!A==true.建边<i,i'>

 

建立一个2N阶的有向图,其中的点分为N对,每对点表示布尔序列A的一个元素的0、1取值(以下将代表A[i]的0取值的点称为i,代表A[i]的1取值的点称为i')。显然每对点必须且只能选取一个。然后,图中的边具有特定含义。若图中存在边<i, j>,则表示若选了i必须选j。可以发现,上面的9种限制关系中,后7种二元限制关系都可以用连边实现,比如NOT(A[x] AND A[y])需要连两条边<x, y'>和<y, x'>,A[x] OR A[y]需要连两条边<x', y>和<y', x>。而前两种一元关系,对于A[x](即x必选),可以通过连边<x',x>来实现,而NOT A[x](即x不能选),可以通过连边<x, x'>来实现

 

常用的限制关系: 
xANDy = 0<=>(x=>y)AND(y=>x) 
xANDy = 1<=>(x=>x)AND(y=>y) 
(
上面这个比较不好想到, 考虑 x,y都要为1,所以保证只要x或y为0都是假的,将x’连到x,y’连到y,使得推出矛盾) 
xORy = 0<=>(x=>x)AND(y=>y) 
xORy = 1<=>(x=>y)AND(y=>x)   注意是 x1->x
xXORy = 0<=>(x=>y)AND(x=>y)AND(y=>x)AND(y=>x) 
xXORy = 1<=>(x=>y)AND(x=>y)AND(y=>x)AND(y=>x)


模型一:两者(A,B)不能同时取

  那么选择了A就只能选择B’,选择了B就只能选择A’
  连边A→B’,B→A’


模型二:两者(A,B)不能同时不取

  那么选择了A’就只能选择B,选择了B’就只能选择A
  连边A’→B,B’→A


模型三:两者(A,B)要么都取,要么都不取

  那么选择了A,就只能选择B,选择了B就只能选择A,选择了A’就只能选择B’,选择了B’就只能选择A’
  连边A→B,B→A,A’→B’,B’→A’


模型四:两者(A,A’)必取A

  那么,那么,该怎么说呢?先说连边吧。
  连边A’→A


#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <stack>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 2e3 + 5;
int n, m, low[maxn], dfn[maxn], id[maxn], scc_cnt, dfs_cnt, cnt[maxn];
vector<int> v[maxn];
stack<int> s;
void init()
{
    memset(low, 0, sizeof(low));
    memset(id, 0, sizeof(id));
    memset(dfn, 0, sizeof(dfn));
    scc_cnt = dfs_cnt = 0;
    for(int i = 0; i < maxn; i++)
        v[i].clear();
    while(!s.empty())
        s.pop();
}
void addedge(int x, int y)
{
    v[x].push_back(y);
}
void tarjan(int x)
{
    dfn[x] = low[x] = ++dfs_cnt;
    s.push(x);
    for(int i = 0; i < v[x].size(); i++)
    {
        int to = v[x][i];
        if(!dfn[to])
        {
            tarjan(to);
            low[x] = min(low[x], low[to]);
        }
        else if(!id[to])
            low[x] = min(low[x], dfn[to]);
    }
    if(low[x] == dfn[x])
    {
        scc_cnt++;
        while(1)
        {
            int u = s.top();
            s.pop();
            id[u] = scc_cnt;
            if(x == u) break;
        }
    }
}
void scc()
{
    for(int i = 1; i <= 2*n ; i++)
        if(!dfn[i])
            tarjan(i);
}
int main()
{
    while(~scanf("%d%d", &n, &m))
    {
        init();
        int a, b, c;
        char cmd[50];
        while(m--)
        {
            scanf("%d%d%d", &a, &b, &c);
            a++, b++;
            scanf(" %s", cmd);
            if(strcmp(cmd, "AND" ) == 0 )
            {
                if(c)
                {
                    addedge(a+n, a); // a&b = 1, a = 1, b = 1
                    addedge(b+n, b);
                }
                else
                {
                    addedge(a, b+n); // a&b = 0, a = 1 && b = 0 || a = 0 && b = 1
                    addedge(b, a+n);
//                    addedge(b+n, a);
//                    addedge(a+n, b);
                }
            }
            if(strcmp(cmd, "OR" ) == 0 )
            {
                if(c)
                {
                    addedge(b+n, a); // a|b = 0, a = 1 && b = 0 || a = 0 && b = 1
                    addedge(a+n, b);
                }
                else
                {
                    addedge(a, a+n); // a|b = 1 , a = 1, b = 1;
                    addedge(b, b+n);
                }
            }
            if(strcmp(cmd, "XOR" ) == 0 )
            {
                if(c)
                {
                    addedge(a, b+n); // a 跟 b 的值不一样就好了
                    addedge(b, a+n);
                    addedge(a+n, b);
                    addedge(b+n, a);
                }
                else
                {
                    addedge(a+n, b+n); // a跟b 的值一样就好了
                    addedge(a, b);
                    addedge(b, a);
                    addedge(b+n, a+n);
                }
            }
        }
        scc();
        int flag = 0;
        for(int i = 1; i <= n; i++)
        {
//            cout << id[i] << ' ' << id[i+n] << endl;
            if(id[i] == id[i+n])
            {
                flag = 1;
                break;
            }
        }
        puts(flag ? "NO" : "YES");
    }
    return 0;
}

题意:平面上,一个圆,圆的边上按顺时针放着n个点。现在要连m条边,比如a,b,那么a到b可以从圆的内部连接,也可以从圆的外部连接。给你的信息中,每个点最多只会连接的一条边。问能不能连接这m条边,使这些边都不相交。

思路:如果两个线段相交但不包涵, 就只能一个连外边,一个连内部,也就是两种状态某种状态不能同时共存

#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <stack>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 2e4 + 5;
int n, m, low[maxn], dfn[maxn], id[maxn], scc_cnt, dfs_cnt, cnt[maxn];
int S[maxn], T[maxn], D[maxn], in[maxn], col[maxn], opp[maxn];
vector<int> v[maxn];
stack<int> s;
struct node
{
    int l, r;
    node(){}
    node(int ll, int rr) : l(ll), r(rr){}
}a[maxn];
void init()
{
    memset(low, 0, sizeof(low));
    memset(id, 0, sizeof(id));
    memset(dfn, 0, sizeof(dfn));
    scc_cnt = dfs_cnt = 0;
    for(int i = 0; i < maxn; i++)
        v[i].clear();
    while(!s.empty())
        s.pop();
}
void addedge(int x, int y)
{
    v[x].push_back(y);
}
void tarjan(int x)
{
    dfn[x] = low[x] = ++dfs_cnt;
    s.push(x);
    for(int i = 0; i < v[x].size(); i++)
    {
        int to = v[x][i];
        if(!dfn[to])
        {
            tarjan(to);
            low[x] = min(low[x], low[to]);
        }
        else if(!id[to])
            low[x] = min(low[x], dfn[to]);
    }
    if(low[x] == dfn[x])
    {
        scc_cnt++;
        while(1)
        {
            int u = s.top();
            s.pop();
            id[u] = scc_cnt;
            if(x == u) break;
        }
    }
}
void scc()
{
    for(int i = 0; i < 2*m ; i++)
        if(!dfn[i])
            tarjan(i);
}

int main()
{
    while(~scanf("%d%d", &n, &m))
    {
        init();
        for(int i = 1; i <= m; i++)
            scanf("%d%d", &a[i].l, &a[i].r);
        for(int i = 1; i <= m; i++)
        {
            for(int j = i+1; j <= m; j++)
            {
                if(a[i].r >= a[j].l && a[i].r < a[j].r && a[i].l < a[j].l
                   || a[i].l > a[j].l && a[i].l < a[j].r && a[i].r > a[j].r)
                {
                    addedge(i, j+m);
                    addedge(j, i+m);
                    addedge(i+m, j);
                    addedge(j+m, i);
                }
            }
        }
        scc();
        int flag = 0;
        for(int i = 0; i < m; i++)
        {
            if(id[i] == id[i+m])
            {
                flag = 1;
                break;
            }
        }
        puts(flag ? "the evil panda is lying again" : "panda is telling the truth...");
    }
    return 0;
}
/*
8 4
0 4
1 7
2 6
3 5

4 2
0 1
3 2
*/




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值