您的位置:首页 > 其它

2-sat建图以及刷题记录~~

2015-09-14 17:41 225 查看
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]

建立有向图。

若图中存在有向边i->j,则表示若选了i必须选j

默认下面的x y都为1 也就是选择~~

And 结果为1:建边 ~x->x, ~y->y (两个数都为1)

And 结果为0:建边 y->~x , x->~y(两个数至少有一个为0)

OR 结果为1:建边 ~x->y , ~y->x(两个数至少有一个为1)

OR 结果为0:建边 x->~x , y->~y(两个数都为0)

XOR 结果为1:建边 x->~y , ~x->y , ~y->x , y -> ~x (两个数一个为0,一个为1)

XOR 结果为0:建边 x->y , ~x->~y , y->x, ~y->~x(两个数同为1或者同为0)

建图主要是参照上述。不过有一些得注意。

建图的一般准则是:

逆向建图:由于2-SAT某一点要么选1要么选0.....那么每次找出有矛盾的两点a、b,进行建图,a->b^1以及b->a^1

这样子就确保了一定选择某一个点以及可以更方便导出矛盾~~~



可参考~~hdu4115

正向建图:任意两点的关系有a-b,a-b^1,a^1-b,a^1-b^1....然后去掉矛盾的关系后,选择那些“只能”这么搭配的关系进行建边。

如去除矛盾关系之后有:a-b,a^1-b,a^1-b^1.这三种关系。已知 a^1、b与对方谁配对都行。但是a只能与b配对、b^1只能与a^1配对。所以建立两条边:a->b,b^1->a^1

[u][/u]

可参见poj2296

一般来说这里建立的是有向图例如hdu3622,hdu1814,但是有时候要根据题意来建立无向图如poj3027。

hdu3622:

有n对点位置,要在每对点里选择一个点放置炸弹,炸弹爆炸范围为一个园,半径为r,求一种选法是的炸弹的爆炸范围r最大且互不炸到~~~

二分枚举半径r+2-SAT判断是否有解。

x与y距离小于2r,那么建立根据对称性建立两条边,x->y^1,y->x^1

但是此时并不意味存在y^1->x这条边,或许他们是存在矛盾的,依靠这个矛盾性才能导出是否有解

#include <math.h>
#include <stdio.h>
#include <string.h>
#define M 210
#define N 100000
#define eps 1e-4
int n,cnt,scnt,top,edgeNum;
int low[M],dfn[M],stack[M],id[M],head[M];
bool instack[M];
struct node{int x,y;}p[M];
struct edge{int v,next;}edge[2*N];

double dist(node p,node q){
return sqrt((double)((p.x-q.x)*(p.x-q.x)+(p.y-q.y)*(p.y-q.y)));
}
void add(int a,int b){
edge[edgeNum].v=b;
edge[edgeNum].next=head[a];
head[a]=edgeNum++;
}
void dfs(int x){
low[x]=dfn[x]=++cnt;
stack[++top]=x;
instack[x]=1;
int v;
for(int i=head[x];i!=-1;i=edge[i].next){
v=edge[i].v;
if(!dfn[v]){
dfs(v);
low[x]=low[v]<low[x]?low[v]:low[x];
}else if(instack[v]&&dfn[v]<low[x]){
low[x]=dfn[v];
}
}
if(low[x]==dfn[x]){
scnt++;
do{
v=stack[top--];
instack[v]=0;
id[v]=scnt;
}while(v!=x);
}
return ;
}
bool solve(){
cnt=scnt=top=0;
memset(dfn,0,sizeof(dfn));
memset(instack,0,sizeof(instack));
for(int i=0;i<n;i++){
if(!dfn[i]) dfs(i);  ///访问,如果没访问过  就访问
}
for(int i=0;i<n;i++){  ///如果i与i^1的状态一样~~
if(id[i]==id[i^1]) return 0;
}
return 1;
}
void init(double r){
edgeNum=0;
memset(id,0,sizeof(id));
memset(head,-1,sizeof(head));
for(int i=0;i<n;i++)
for(int j=i%2==0?i+2:i+1;j<n;j++){
if(dist(p[i],p[j])<=2*r){  ///冲突了   不能同时要
add(i,j^1);    ///选择i 不选择j  也就是选择j^1
add(j,i^1);     ///选择j 不选择i  也就是选择i^1
}
}
}
int main(){
while(scanf("%d",&n)!=EOF){
n<<=1;
for(int i=0;i<n;i+=2)
scanf("%d%d%d%d",&p[i].x,&p[i].y,&p[i^1].x,&p[i^1].y);

double left=0,right=40000;
while(right-left>eps){
double mid=(left+right)/2;
init(mid);
if(solve()) left=mid;
else right=mid;
}
printf("%.2lf\n",left);
}
return 0;
}


poj3207:
有n个点均匀分布在一个圆上,有m个边连接a与b,这条边可以画在园外也可以画在园内,问是否有一种画法使得画完m个边且这些边不相交~~~

判断任意两条边x、y是否相交(没有包含),如果相交,就建立两条边,x->y^1, y->x^1

但是这里可以推出,选择y^1页必须选择x,所以还要加上两条边 y^1->x x^1->y 这样一来就相当于无向图了~~

这里需要好好体味一下~~

#include <algorithm>
#include <iostream>
#include<string.h>
#include <fstream>
#include <math.h>
#include <vector>
#include <cstdio>
#include <string>
#include <queue>
#include <map>
#include <set>
#define exp 1e-8
#define fi first
#define se second
#define ll long long
#define INF 0x3f3f3f3f
#define pb(a) push_back(a)
#define mp(a,b) make_pair(a,b)
#define all(a) a.begin(),a.end()
#define mm(a,b) memset(a,b,sizeof(a));
#define for1(a,b) for(int a=1;a<=b;a++)//1---(b)
#define rep(a,b,c) for(int a=b;a<=c;a++)//b---c
#define repp(a,b,c)for(int a=b;a>=c;a--)///
#define stl(c,itr) for(__typeof((c).begin()) itr=(c).begin();itr!=(c).end();itr++)
using namespace std;
void bug(string m="here"){cout<<m<<endl;}
template<typename __ll> inline void READ(__ll &m){__ll x=0,f=1;char ch=getchar();while(!(ch>='0'&&ch<='9')){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}m=x*f;}
template<typename __ll>inline void read(__ll &m){READ(m);}
template<typename __ll>inline void read(__ll &m,__ll &a){READ(m);READ(a);}
template<typename __ll>inline void read(__ll &m,__ll &a,__ll &b){READ(m);READ(a);READ(b);}
template<typename __ll>inline void read(__ll &m,__ll &a,__ll &b,__ll &c){READ(m);READ(a);READ(b);READ(c);}
template<typename __ll>inline void read(__ll &m,__ll &a,__ll &b,__ll &c,__ll &d){READ(m);READ(a);READ(b);READ(c);read(d);}
template < class T > T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }
template < class T > T lcm(T a, T b) { return a / gcd(a, b) * b; }
template < class T > inline void rmin(T &a, const T &b) { if(a > b) a = b; }
template < class T > inline void rmax(T &a, const T &b) { if(a < b) a = b; }
template < class T > T pow(T a, T b) { T r = 1; while(b > 0) { if(b & 1) r = r * a; a = a * a; b /= 2; } return r; }
template < class T > T pow(T a, T b, T mod) { T r = 1; while(b > 0) { if(b & 1) r = r * a % mod; a = a * a % mod; b /= 2; } return r; }

#define M 11000
#define N 500000
#define eps 1e-4
int n,m,cnt,scnt,top,edgeNum;
int low[M],dfn[M],stack[M],id[M],head[M];
bool instack[M];
int dat[5100][2];
struct edge{int v,next;}edge
;

void addedge(int a,int b)
{
//cout<<a<<" "<<b<<endl;
edge[edgeNum].v=b;
edge[edgeNum].next=head[a];
head[a]=edgeNum++;

edge[edgeNum].v=a;
edge[edgeNum].next=head;
head[b]=edgeNum++;
}
void dfs(int x)
{
low[x]=dfn[x]=++cnt;
stack[++top]=x;
instack[x]=1;
int v;
for(int i=head[x];i!=-1;i=edge[i].next)
{
v=edge[i].v;
if(!dfn[v])
{
dfs(v);
low[x]=low[v]<low[x]?low[v]:low[x];
}
else if(instack[v]&&dfn[v]<low[x])
low[x]=dfn[v];
}
if(low[x]==dfn[x]){
scnt++;
do{
v=stack[top--];
instack[v]=0;
id[v]=scnt;
}while(v!=x);
}
return ;
}
bool solve(int n){
cnt=scnt=top=0;
memset(dfn,0,sizeof(dfn));
memset(instack,0,sizeof(instack));
for(int i=0;i<n;i++)
if(!dfn[i]) dfs(i);  ///访问,如果没访问过  就访问
for(int i=0;i<n;i++)///如果i与i^1的状态一样~~
if(id[i]==id[i^1]) return 0;
return 1;
}
bool ok(int i,int j)
{
int a=min(dat[i][0],dat[i][1]),b=max(dat[i][0],dat[i][1]);
int c=min(dat[j][0],dat[j][1]),d=max(dat[j][0],dat[j][1]);
if((c>=b||c<=a)&&d>=a&&d<=b)return 1;
if((d>=b||d<=a)&&c>=a&&c<=b)return 1;
return 0;
}
void init()
{
edgeNum=0;
memset(id,0,sizeof(id));
memset(head,-1,sizeof(head));
for(int i=0;i<m;i++)
for(int j=i+1;j<m;j++)
if(ok(i+1,j+1))
{
addedge((i*2),(j*2)^1);
addedge((j*2),(i*2)^1);
} ///k%2==0 取内部  ==1取外面
}
int main(){
while(cin>>n>>m)
{
for1(i,m)read(dat[i][0],dat[i][1]);
init();
if(solve(m<<1))
cout<<"panda is telling the truth...\n";
else cout<<"the evil panda is lying again\n";
}

return 0;
}


zoj3656...

题目,给出一个b

是由a
根据某种and or xor规则得到的,问任意给一个b

,是否有a
可以生成

好吧,这道题一开始还真不知道怎么做。

[b]原来要以二进制的眼光去看待这道题~~~~


毕竟每次and or xor都可以独立位进行看待。

所以进行32次2-sat判断即可。建图方式可以参照最顶层~~

#include <algorithm>
#include <iostream>
#include<string.h>
#include <fstream>
#include <math.h>
#include <vector>
#include <cstdio>
#include <string>
#include <queue>
#include <map>
#include <set>
#define exp 1e-8
#define fi first
#define se second
#define ll long long
#define INF 0x3f3f3f3f
#define pb(a) push_back(a)
#define mp(a,b) make_pair(a,b)
#define all(a) a.begin(),a.end()
#define mm(a,b) memset(a,b,sizeof(a));
#define for1(a,b) for(int a=1;a<=b;a++)//1---(b)
#define rep(a,b,c) for(int a=b;a<=c;a++)//b---c
#define repp(a,b,c)for(int a=b;a>=c;a--)///
#define stl(c,itr) for(__typeof((c).begin()) itr=(c).begin();itr!=(c).end();itr++)
using namespace std;
void bug(string m="here"){cout<<m<<endl;}
template<typename __ll> inline void READ(__ll &m){__ll x=0,f=1;char ch=getchar();while(!(ch>='0'&&ch<='9')){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}m=x*f;}
template<typename __ll>inline void read(__ll &m){READ(m);}
template<typename __ll>inline void read(__ll &m,__ll &a){READ(m);READ(a);}
template<typename __ll>inline void read(__ll &m,__ll &a,__ll &b){READ(m);READ(a);READ(b);}
template<typename __ll>inline void read(__ll &m,__ll &a,__ll &b,__ll &c){READ(m);READ(a);READ(b);READ(c);}
template<typename __ll>inline void read(__ll &m,__ll &a,__ll &b,__ll &c,__ll &d){READ(m);READ(a);READ(b);READ(c);read(d);}
template < class T > T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }
template < class T > T lcm(T a, T b) { return a / gcd(a, b) * b; }
template < class T > inline void rmin(T &a, const T &b) { if(a > b) a = b; }
template < class T > inline void rmax(T &a, const T &b) { if(a < b) a = b; }
template < class T > T pow(T a, T b) { T r = 1; while(b > 0) { if(b & 1) r = r * a; a = a * a; b /= 2; } return r; }
template < class T > T pow(T a, T b, T mod) { T r = 1; while(b > 0) { if(b & 1) r = r * a % mod; a = a * a % mod; b /= 2; } return r; }

#define M 510*4
#define N 500*500
int n,cnt,scnt,top,edgeNum;
int g[510][510];
int low[M],dfn[M],stack[M],id[M],head[M];
bool instack[M];
struct node{int x,y;}p[M];
struct edge{int v,next;}edge[10*N];

void addedge(int a,int b){
edge[edgeNum].v=b;
edge[edgeNum].next=head[a];
head[a]=edgeNum++;
}
void dfs(int x){
low[x]=dfn[x]=++cnt;
stack[++top]=x;
instack[x]=1;
int v;
for(int i=head[x];i!=-1;i=edge[i].next){
v=edge[i].v;
if(!dfn[v]){
dfs(v);
low[x]=low[v]<low[x]?low[v]:low[x];
}else if(instack[v]&&dfn[v]<low[x]){
low[x]=dfn[v];
}
}
if(low[x]==dfn[x]){
scnt++;
do{
v=stack[top--];
instack[v]=0;
id[v]=scnt;
}while(v!=x);
}
return ;
}
bool solve(){
cnt=scnt=top=0;
memset(dfn,0,sizeof(dfn));
memset(instack,0,sizeof(instack));
for(int i=0;i<2*n;i++){
if(!dfn[i]) dfs(i);  ///访问,如果没访问过  就访问
}
for(int i=0;i<2*n;i++){  ///如果i与i^1的状态一样~~
if(id[i]==id[i^1]) return 0;
}
return 1;
}
void init(int k){
edgeNum=0;
memset(id,0,sizeof(id));
memset(head,-1,sizeof(head));
for(int i=0;i<n;i++)   /// 2*i  取1  2*i+1 取0
for(int j=0;j<n;j++)
{
if(i==j)continue;
if(i%2==1&&j%2==1)/// or
{
if((g[i][j]>>k)&1==1)
{
addedge(2*i+1,2*j);
addedge(2*j+1,2*i);
}
else
{
addedge(2*i,2*i+1);
addedge(2*j,2*j+1);
}
}
else if(i%2==0&&j%2==0)/// and
{
if((g[i][j]>>k)&1==1)
{
addedge(2*i+1,2*i);
addedge(2*j+1,2*j);
}
else
{
addedge(2*j,2*i+1);
addedge(2*i,2*j+1);
}
}
else
{
if((g[i][j]>>k)&1==1)
{
addedge(2*i,2*j+1);
addedge(2*i+1,2*j);
addedge(2*j+1,2*i);
addedge(2*j,2*i+1);
}
else
{
addedge(2*i,2*j);
addedge(2*i+1,2*j+1);
addedge(2*j,2*i);
addedge(2*j+1,2*i+1);
}
}
}
}
int main()
{
while(cin>>n)
{
bool flag=0;
rep(i,0,n-1)rep(j,0,n-1)read(g[i][j]);
rep(i,0,n-1)if(g[i][i]!=0)flag=1;
if(flag){puts("NO");continue;}
for(int tt=1;tt<=32;tt++)
{
init(tt-1);
if(!solve())
{
flag=1;
break;
}
}
if(flag)puts("NO");
else puts("YES");
}
}


hdu4115:

题意知道某一个人n轮出剪刀或者石头还是布~~

然后给你m个制约,第a轮与第b轮猜拳方式要么相同要么不相同

问满足这个 条件的情况下你能否获胜,也就是说每一轮要么平要么赢 没有一轮输~~~

这道题属于2-SAT...

因为对于你来说,每一轮要么选平局0要么选赢1...

然后由题目的意思进行矛盾判断进行建图~~

#include <algorithm>
#include <iostream>
#include<string.h>
#include <fstream>
#include <math.h>
#include <vector>
#include <cstdio>
#include <string>
#include <queue>
#include <map>
#include <set>
#define exp 1e-8
#define fi first
#define se second
#define ll long long
#define INF 0x3f3f3f3f
#define mp(a,b) make_pair(a,b)
#define all(a) a.begin(),a.end()
#define mm(a,b) memset(a,b,sizeof(a));
#define for1(a,b) for(int a=1;a<=b;a++)//1---(b)
#define rep(a,b,c) for(int a=b;a<=c;a++)//b---c
#define repp(a,b,c)for(int a=b;a>=c;a--)///
#define stl(c,itr) for(__typeof((c).begin()) itr=(c).begin();itr!=(c).end();itr++)
using namespace std;
void bug(string m="here"){cout<<m<<endl;}
template<typename __ll> inline void READ(__ll &m){__ll x=0,f=1;char ch=getchar();while(!(ch>='0'&&ch<='9')){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}m=x*f;}
template<typename __ll>inline void read(__ll &m){READ(m);}
template<typename __ll>inline void read(__ll &m,__ll &a){READ(m);READ(a);}
template<typename __ll>inline void read(__ll &m,__ll &a,__ll &b){READ(m);READ(a);READ(b);}
template<typename __ll>inline void read(__ll &m,__ll &a,__ll &b,__ll &c){READ(m);READ(a);READ(b);READ(c);}
template<typename __ll>inline void read(__ll &m,__ll &a,__ll &b,__ll &c,__ll &d){READ(m);READ(a);READ(b);READ(c);read(d);}
template < class T > T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }
template < class T > T lcm(T a, T b) { return a / gcd(a, b) * b; }
template < class T > inline void rmin(T &a, const T &b) { if(a > b) a = b; }
template < class T > inline void rmax(T &a, const T &b) { if(a < b) a = b; }
template < class T > T pow(T a, T b) { T r = 1; while(b > 0) { if(b & 1) r = r * a; a = a * a; b /= 2; } return r; }
template < class T > T pow(T a, T b, T mod) { T r = 1; while(b > 0) { if(b & 1) r = r * a % mod; a = a * a % mod; b /= 2; } return r; }

#define M 60000
#define N 60000
int n,m,cnt,scnt,top,edgeNum;
int low[M],dfn[M],stack[M],id[M],head[M];
bool instack[M];
int dat[M];
struct edge{int v,next;}edge[5*N];

void addedge(int a,int b){
edge[edgeNum].v=b;
edge[edgeNum].next=head[a];
head[a]=edgeNum++;
}
void dfs(int x){
low[x]=dfn[x]=++cnt;
stack[++top]=x;
instack[x]=1;
int v;
for(int i=head[x];i!=-1;i=edge[i].next){
v=edge[i].v;
if(!dfn[v]){
dfs(v);
low[x]=low[v]<low[x]?low[v]:low[x];
}else if(instack[v]&&dfn[v]<low[x]){
low[x]=dfn[v];
}
}
if(low[x]==dfn[x]){
scnt++;
do{
v=stack[top--];
instack[v]=0;
id[v]=scnt;
}while(v!=x);
}
return ;
}
bool solve(int n){
cnt=scnt=top=0;
memset(dfn,0,sizeof(dfn));
memset(instack,0,sizeof(instack));
for(int i=0;i<n;i++){
if(!dfn[i]) dfs(i);  ///访问,如果没访问过  就访问
}
for(int i=0;i<n;i++){  ///如果i与i^1的状态一样~~
if(id[i]==id[i^1]) return 0;
}
return 1;
}
void init(){
edgeNum=0;
memset(id,0,sizeof(id));
memset(head,-1,sizeof(head));
}
int main()
{
int cas;
read(cas);
for1(tt,cas)
{
read(n,m);
rep(i,0,n-1)read(dat[i]);
rep(i,0,n-1)dat[i]--;
init();
bool flag=0;
while(m--)
{
int a,b,c;
read(a,b,c);
a--,b--;
if(flag||(a==b&&c==0))continue;
if(a==b&&c==1){flag=1;continue;}
int wina=(dat[a]+1)%3,losea=dat[a];
int winb=(dat[b]+1)%3,loseb=dat[b];
if(c==0)//要找相同的出拳方式
{//矛盾点就是出拳方式不一样
if(wina!=winb)addedge(2*a,2*b+1),addedge(2*b,2*a+1);//那只能选择另一方~~~
if(wina!=loseb)addedge(2*a,2*b),addedge(2*b+1,2*a+1);
if(losea!=winb)addedge(2*a+1,2*b+1),addedge(2*b,2*a);
if(losea!=loseb)addedge(2*a+1,2*b),addedge(2*b+1,2*a);
}
else///要找出不同的出拳方式
{///矛盾点就是出拳方式一样
if(wina==winb)addedge(2*a,2*b+1),addedge(2*b,2*a+1);//那只能选择另一方~~~
if(wina==loseb)addedge(2*a,2*b),addedge(2*b+1,2*a+1);
if(losea==winb)addedge(2*a+1,2*b+1),addedge(2*b,2*a);
if(losea==loseb)addedge(2*a+1,2*b),addedge(2*b+1,2*a);
}
}
if(flag||solve(n*2)==0)printf("Case #%d: no\n",tt);
else printf("Case #%d: yes\n",tt);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: