2-SAT

昨天学弟问我G题是不是2-SAT,尴尬的是我不知道那是什么,所以今天特地来学一下,先来个简单的

先看一下最经典的题HDU1814和平委员会

题意:输入n,m代表n个国家,每个国家 i 有两个代表分别是 2*i-1和2*i.m代表有m对冤家,每对冤家不能一起出现在委员会中,问能不能让所有国家都出一个代表构成委员会,可以的话输出n个国家所选出的代表标号,否则输出NIE
就粘一下输入输出吧
Sample Input
3 2
1 3
2 4
Sample Output
1
4
5

这道题感觉跟2-SAT(仅限我现在理解的)有关系,还感觉没啥关系

oth(x)代表和x是一个国家的那个人的编号

做法就是如果 i 和 j 是冤家那么 选 i 就要选 oth(j) ,选 j 就要选 oth(i) 然后从1~2*n开始遍历

数组 f 初始是0,1代表选中,2代表不选

对于第 i 个遇到 f[i] 是0,那么先试试 选 i ,即 f[i]=1,f[oth(i)]=2

如果选了一个那么边相连的就都要选,如果边相连是2了,说明决策不对则f[i]=2,f[oth(i)]=1,若还不行那么无解

最后把f[i]=1的 i 输出就可以了

#include<stdio.h>
#include<string.h>
#define rep(i,j,k) for(int i=j;i<=k;++i)
#define oth(x) (x%2==0?x-1:x+1)
struct s
{
    int to,next;
} edge[40005];
int head[16005];
int cnt;
void add(int x,int y)
{
    edge[cnt].to=y;
    edge[cnt].next=head[x];
    head[x]=cnt++;
}
int tot;
int f[16005],ff[160005];
bool paint(int x)
{
    if(f[x]!=0)
        return f[x]%2;
    f[x]=1;
    f[oth(x)]=2;
    ff[tot++]=x;
    for(int i=head[x]; i!=-1; i=edge[i].next)
    {
        if(!paint(edge[i].to))
            return false;
    }
    return true;
}
int n;
bool solve()
{
    rep(i,1,2*n)
    {
        if(f[i])
            continue;
        tot=0;
        if(!paint(i))
        {
            rep(j,0,tot-1)
            {
                f[ff[j]]=0;
                f[oth(ff[j])]=0;
            }
            if(!paint(oth(i)))
                return false;
        }
    }
    return true;
}
int main()
{
    int m;
    while(scanf("%d %d",&n,&m)!=EOF)
    {
        memset(head,-1,sizeof(head));
        memset(f,0,sizeof(f));
        cnt=0;
        int x,y;
        rep(i,1,m)
        {
            scanf("%d %d",&x,&y);//oth(x)是x的搭档
            add(x,oth(y));//选x就只能选y的搭档
            add(y,oth(x));
        }
        if(solve())
        {
            rep(i,1,2*n)
            if(f[i]==1)
                printf("%d\n",i);
        }
        else
            printf("NIE\n");
    }
}

 

posted @ 2018-06-03 17:25  注册以后还能改吧  阅读(295)  评论(0编辑  收藏  举报