zoukankan      html  css  js  c++  java
  • poj 3648 2-SAT问题

    思路:将每对夫妻看成是对立状态,每个不正常关系都是一个矛盾,按2-SAT的方式建边。最后建一条新娘到新郎的边。具体看注释

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<queue>
    #define Maxn 62
    #define Maxm Maxn*Maxn
    using namespace std;
    int vi[Maxn],head[Maxn],dfn[Maxn],low[Maxn],e,n,lab,top,num,id[Maxn],Stack[Maxn],in[Maxn],Hash[Maxn],col[Maxn];
    struct Edge{
        int u,v,next;
    }edge[Maxm];
    void init()//初始化
    {
        memset(vi,0,sizeof(vi));
        memset(head,-1,sizeof(head));
        memset(dfn,0,sizeof(dfn));
        memset(low,0,sizeof(low));
        memset(id,0,sizeof(id));
        memset(in,0,sizeof(in));
        memset(col,0,sizeof(col));
        e=lab=top=num=0;
    }
    void add(int u,int v)//加边
    {
        edge[e].u=u,edge[e].v=v,edge[e].next=head[u],head[u]=e++;
    }
    void Tarjan(int u)//找出强连通分支
    {
        int i,j,v;
        dfn[u]=low[u]=++lab;
        Stack[top++]=u;
        vi[u]=1;
        for(i=head[u];i!=-1;i=edge[i].next)
        {
            v=edge[i].v;
            if(!dfn[v])
            {
                Tarjan(v);
                low[u]=min(low[u],low[v]);
            }
            if(vi[v])
            low[u]=min(low[u],dfn[v]);
    
        }
        if(low[u]==dfn[u])
        {
            ++num;
            do{
                i=Stack[--top];
                vi[i]=0;
                id[i]=num;
            }while(i!=u);
        }
    }
    void buildGraphic()//缩点后重新建树,以便进行拓扑排序
    {
        int ed=e,u,v;
        memset(head,-1,sizeof(head));
        e=0;
        int i;
        for(i=0;i<ed;i++)
        {
            u=edge[i].u;
            v=edge[i].v;
            if(id[u]!=id[v])
            {
                add(id[v],id[u]);//由于2-SAT问题中是找出度为0的点,这里我们建个反图,方便进行拓扑排序,变成找入度为0的点
                in[id[u]]++;
            }
        }
    }
    void Topsort()
    {
        int i,j,u,v,temp;
        queue<int> q;
        int fron,rear;
        fron=rear=0;
        for(i=1;i<=num;i++)
            if(in[i]==0)
                q.push(i);
        while(!q.empty())
        {
            temp=q.front();
            q.pop();
            if(!col[temp]) col[temp]=1,col[Hash[temp]]=2;//如果该连通分支未着色,那么给他着1,它的对立点就必须着不同的色
            for(i=head[temp];i!=-1;i=edge[i].next)
            {
                v=edge[i].v;
                in[v]--;
                if(in[v]==0)
                    q.push(v);
            }
        }
    }
    int solve()
    {
        int i,j;
        for(i=1;i<=2*n;i++)
            if(!dfn[i])
            Tarjan(i);
        for(i=1;i<=n;i++)
            if(id[i]==id[i+n])
                return 0;//有矛盾则结束
            else
                Hash[id[i]]=id[i+n],Hash[id[i+n]]=id[i];//标记每个连通分支间的对立关系,即不能再同一侧
    
        buildGraphic();
        Topsort();
        for(i=2;i<=n;i++)
            if(col[id[i]]==col[id[n+1]]) printf("%dh ",i-1);//输出和新娘同侧的人
            else printf("%dw ",i-1);
        printf("
    ");
        return 1;
    }
    int op(int x)
    {
        if(x<=n) return x+n;
        return x-n;
    }
    int main()
    {
        int m,i,j,a,b;
        char c1,c2;
        while(scanf("%d%d",&n,&m),n|m)
        {
            init();
            for(i=0;i<m;i++)
            {
                scanf("%d%c%d%c",&a,&c1,&b,&c2);//husband点为1~N,wife点为N+1~2*N
                a++,b++;
                if(c1=='w')
                    a+=n;
                if(c2=='w')
                    b+=n;
                add(a,op(b));
                add(b,op(a));
            }//我们的目标是选择和新郎同一侧的,新郎固然在自己一侧。
            add(n+1,1);//建一条由新娘到新郎的边,若选择了新娘,新郎也会被选,新郎和新娘成了同侧,便矛盾。即新娘不能和新郎同一侧
            if(!solve())
                printf("bad luck
    ");
        }
        return 0;
    }
  • 相关阅读:
    创建用户自定义函数 SQL
    sql with as 用法
    将string转为同名类名,方法名。(c#反射)
    linq 实现对象映射
    sql 游标
    C#编程总结(六)异步编程
    线程加锁解锁,用于循环条件不确定大小
    滚动条随代码滚动
    C# 代码小技巧
    reload方法
  • 原文地址:https://www.cnblogs.com/wangfang20/p/3221049.html
Copyright © 2011-2022 走看看