现有一个由N个布尔值组成的序列A,给出一些限制关系,比如A[x] AND A[y]=0、A[x] OR A[y] OR A[z]=1、A[x] XOR A[y]=0等,要确定A[0..N-1]的值,使得其满足所有限制关系。这个称为SAT问题,特别的,若每种限制关系中最多只对两个元素进行限制,则称为2-SAT问题。
对于x、y有11种关系,将其拆点2x(假),2x+1(真).对于x为假或者y为假这样的条件,我们连两条有向边2x+1->2y、2y+1->2x,注意这里是有向边以及边的起点和终点的关系要确定。同理,我们可以将很多关系都确定,建立一个有向图,进行深搜,可以O(nm)得到解。
hdu3622bomb
题目大意:有n对点,每对点中选择一个,最后确定一个实数r,以选出的每一个点做圆心,r做半径,保证圆不相交,求最大的r。
思路:这里的一对点就相当于上面拆出来的点。我们二分答案进行求解,就用2sat问题进行可行性判断,对于距离<二分答案的点相应的连边(注意有向边这一要求)。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define maxnode 1010 #define sta 1000 using namespace std; double xi[maxnode],yi[maxnode]; int dis[maxnode][maxnode]={0},s[maxnode]={0},stot=0,n,point[maxnode]={0},next[maxnode*maxnode*2]={0}, en[maxnode*maxnode*2]={0},tot=0; bool visit[maxnode]={false}; void add(int u,int v) { ++tot;next[tot]=point[u];point[u]=tot;en[tot]=v; } void pre(int mid) { int i,j; memset(point,0,sizeof(point));tot=0; for (i=0;i<n;++i) for (j=i+1;j<n;++j) { if (dis[i<<1][j<<1]<mid){add(i<<1,j<<1|1);add(j<<1,i<<1|1);} if (dis[i<<1][j<<1|1]<mid) {add(i<<1,j<<1);add(j<<1|1,i<<1|1);} if (dis[i<<1|1][j<<1]<mid) {add(j<<1,i<<1);add(i<<1|1,j<<1|1);} if (dis[i<<1|1][j<<1|1]<mid) {add(i<<1|1,j<<1);add(j<<1|1,i<<1);} } } bool dfs(int x) { int i; if (visit[x^1]) return false; if (visit[x]) return true; visit[x]=true;s[++stot]=x; for (i=point[x];i;i=next[i]) if (!dfs(en[i])) return false; return true; } bool judge(int mid) { int i,j,sum=0; pre(mid);memset(visit,false,sizeof(visit)); for (i=0;i<n*2;i+=2) { if (!visit[i]&&!visit[i+1]) { stot=0; if (!dfs(i)) { while(stot) { visit[s[stot]]=false;--stot; } if (!dfs(i+1)) return false; } } } return true; } int main() { freopen("bomb.in","r",stdin); freopen("bomb.out","w",stdout); int m,i,j,l,r,mid; scanf("%d",&n); for (i=0;i<n;++i) { scanf("%lf%lf%lf%lf",&xi[i*2],&yi[i*2],&xi[i*2+1],&yi[i*2+1]); } for (i=0;i<2*n;++i) for (j=0;j<2*n;++j) dis[i][j]=(int)(sqrt((xi[i]-xi[j])*(xi[i]-xi[j])+(yi[i]-yi[j])*(yi[i]-yi[j]))*sta); l=0;r=40000*sta; while(l<r) { mid=(r+l)/2; if (judge(mid)) l=mid+1; else r=mid; } printf("%.2f ",(double)l/(double)(sta*2)); fclose(stdin); fclose(stdout); }
bzoj1823 满汉全席
题目大意:给定n种菜,m个条件,然后判断能否做出一套菜满足所有的条件。
思路:裸的2—sat问题。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxm 10005 using namespace std; int point[maxm],next[maxm],en[maxm],zh[maxm],tot,n; bool mark[maxm]; char ch[maxm]; void add(int u,int v){next[++tot]=point[u];point[u]=tot;en[tot]=v;} bool dfs(int u){ int i,j,v; if (mark[u^1]) return false; if (mark[u]) return true; mark[u]=true;zh[++zh[0]]=u; for (i=point[u];i;i=next[i]) if (!dfs(v=en[i])) return false; return true; } bool solve(){ int i,j; for (i=2;i<=n*2;i+=2){ if (!mark[i]&&!mark[i^1]){ zh[0]=0; if (!dfs(i)){ while(zh[0]) mark[zh[zh[0]--]]=false; if (!dfs(i^1)) return false; } } }return true; } int main(){ int i,j,m,t,x,y,l;scanf("%d",&t); while(t--){ scanf("%d%d",&n,&m);tot=0; memset(mark,false,sizeof(mark)); memset(point,0,sizeof(point)); memset(next,0,sizeof(next)); for (i=1;i<=m;++i){ scanf("%s",&ch);l=strlen(ch); for (x=0,j=1;j<l;++j) x=x*10+ch[j]-'0'; if (ch[0]=='m') x=x*2; else x=x*2+1; scanf("%s",&ch);l=strlen(ch); for (y=0,j=1;j<l;++j) y=y*10+ch[j]-'0'; if (ch[0]=='m') y=y*2; else y=y*2+1; add(y,x^1);add(x,y^1); }if (!solve()) printf("BAD "); else printf("GOOD "); } }