本文主要是介绍2-sat建图以及刷题记录~~,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
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
可参见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^1add(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;
}
有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[N];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[b];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[n][n] 是由a[n]根据某种and or xor规则得到的,问任意给一个b[n][n],是否有a[n]可以生成
好吧,这道题一开始还真不知道怎么做。
原来要以二进制的眼光去看待这道题~~~~
毕竟每次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 取0for(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;
}
这篇关于2-sat建图以及刷题记录~~的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!