我的第一道2-cat题
2-cat问题解答:http://wenku.baidu.com/view/31fd7200bed5b9f3f90f1ce2.html
http://hi.baidu.com/godforbidyy/blog/item/40efcc33e3bfc409eac4af4f.html
转:(2-cat解题技巧)
1.构图(重点+难点)
2.求图的极大强连通子图 (模板)
3.把每个子图收缩成单个节点,根据原图关系构造一个有向无环图 (模板)
4.判断是否有解,无解则输出(退出) (这块常用到二分枚举答案,下面会详细讲解)
5.对新图进行拓扑排序 (模板)
6.自底向上进行选择、删除 (模板)
7.输出(模板)
题意:
一些点,点的取值可以是0或者1,没有告诉你具体取值。
一些边,有权值,有运算方式(并,或,异或),要求和这条边相连的两个点经过边上的运算后的结果是边的权值。
问你有没有可能把每个点赋值满足所有边的要求。
解:
每个点只有0,1两种值,并且和边对面的点有约束条件,所以可以转化为2-sat问题。i*2和2*i+1为第i个数。
and 等于 1时,要求i和j必须为1,不能为0.
因为是必须选择的,所以如果and ==1 那么,如果两个点都为0,则不可能,就把0连到1上面,在用个一个集合的时候。
或的时候,要选0 必须把1连到0上面
AND b = 1 <==> _a->a _b->b
AND b = 0 <==> _a->_b _b->_a//注意
OR b = 1 <==> _a->b _b->a//因为OR的时候例如a or b时,当a==1时,b就不判了。
OR b = 0 <==> a->_a b->_b
XOR b = 1 <==> _a->b a->_b _b->a b->_a
XOR b = 0 <==> a->b _a->_b b->a _b->_a
自己的代码:
#include<cstdio>
#include<cstdlib>
#include<vector>
#include<queue>
#include<stack>
#include<iostream>
#include<cstring>
using namespace std;
const int NN = 2089;
int dfn[NN];
int low[NN];
int tmp;
int cnt;
int fa[NN];
vector<int>mp[NN];
int n,m;
int vis[NN];
stack<int>mystack;
void init()
{
for(int i=0;i<n*2+5;i++)
mp[i].clear();
tmp=0;
cnt=0;
}
void tarjan(int u)//求强连通
{
vis[u]=1;
dfn[u]=low[u]=tmp++;
mystack.push(u);
for(int i=0;i<mp[u].size();i++)
{
int v=mp[u][i];
if(!dfn[v])
{
tarjan(v);
low[u]=min(low[v],low[u]);
}
else if(vis[v])
low[u]=min(dfn[v],low[u]);
}
if(dfn[u]==low[u])
{
cnt++;
int t;
do
{
t=mystack.top();
mystack.pop();
vis[t]=0;
fa[t]=cnt;
}while(t!=u);
}
}
int main()
{
int a,b,c;
string str;
scanf("%d%d",&n,&m);
init();
for(int i=0;i<m;i++)
{
scanf("%d%d%d",&a,&b,&c);
cin>>str;
if(str=="AND")
{
if(c==1)
{
mp[2*a+1].push_back(2*a);
mp[2*b+1].push_back(2*b);
}
else if(c==0)
{
mp[2*a].push_back(2*b+1);
mp[2*b].push_back(2*a+1);
}
}
if(str=="OR")
{
if(c==1)
{
mp[2*a+1].push_back(2*b);
mp[2*b+1].push_back(2*a);
}
else
{
mp[2*a].push_back(2*a+1);
mp[2*b].push_back(2*b+1);
}
}
if(str=="XOR")
{
if(c==1)
{
mp[2*a].push_back(2*b+1);
mp[2*b].push_back(2*a+1);
mp[2*a+1].push_back(2*b);
mp[2*b+1].push_back(2*a);
}
else
{
mp[2*a+1].push_back(2*b+1);
mp[2*b+1].push_back(2*a+1);
mp[2*a].push_back(2*b);
mp[2*b].push_back(2*a);
}
}
}
for(int i=0;i<n*2;i++)
{
if(!dfn[i])
tarjan(i);
}
int flag=1;
for(int i=0;i<n;i++)
{
if(fa[i*2]==fa[i*2+1])//i*2和i*2+1不能在同一连通分量
{flag=0;break;}
}
if(flag)
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
return 0;
}