Time Limit: 1000MS | Memory Limit: 65536K | |
Total Submissions: 10545 | Accepted: 3912 |
Description
Katu Puzzle is presented as a directed graph G(V, E) 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 ≤ Xi ≤ 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:
|
|
|
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 a (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
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
*/