2—SAT问题

现有一个由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\n",(double)l/(double)(sta*2));
    
    fclose(stdin);
    fclose(stdout);
}
View Code

 

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\n");
        else printf("GOOD\n");
    }
}
View Code

 

posted @ 2015-06-11 15:56  Rivendell  阅读(227)  评论(0编辑  收藏  举报