zoukankan      html  css  js  c++  java
  • 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
    ",(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
    ");
            else printf("GOOD
    ");
        }
    }
    View Code
  • 相关阅读:
    并查集-B
    ->的用法
    PTA-1042 字符统计
    PAT 1040有几个PAT
    assembly x86(nasm)修改后的日常
    python接口自动化之操作常用数据库mysql、oracle
    os模块常用方法
    python 多线程编程并不能真正利用多核的CPU
    连接mysql数据库
    python之用yagmail模块发送邮件
  • 原文地址:https://www.cnblogs.com/Rivendell/p/4569319.html
Copyright © 2011-2022 走看看