• ACM POJ 3648 Wedding(2SAT入门)


    题目链接:http://poj.org/problem?id=3648

    本文作者:kuangbin

    (转载请注明出处,博客:www.cnblogs.com/kuangbin)

     

    【题目大意】很多对夫妇参加一对新人的婚礼。分别做在长桌子的两侧。新郎、新娘分别坐两侧,新娘只能看到她对面的人。新娘不想看到她对面有夫妇。

    而且有一些人是有通奸关系的(男的和男的有,女的和男的、女的和女的都可能有,而且新郎也可能和别人有通奸关系),新娘不想看到有通奸关系一对人。

    也就是有通奸关系的不能一起坐在新娘对面。

    输入是:_n对夫妇(包括新郎新娘在女的,编号为0-(n-1),新郎、新娘那一对的编号为0),_m对通奸关系。

    接下来_m行有通奸关系的。h表示男的,w表是女的,3w 5h即表示第三对夫妇的女的和第五对夫妇的男的有不寻常关系。

     

    【题目类别】2-SAT

    【题目分析】题目就是需要选出包括新郎在内的一半人坐在新娘对面去,选出来的人不能有夫妇,不能有通奸关系的一对人。

    编号分别为0-2*_n,奇数是男的,偶数是女的。自然0是新娘,1是新郎了。

    为了一定选到1,可以加一条边0->1,这样选了新娘就必定会选当新郎,从而判断错误。这样如果有符合题意的_n个人选出来,

    则选出来的肯定包括新郎了。

    注意的是输出的时候是输出和新娘坐同一边的人。即反过来输出就可以了;

     

    代码:

    /*
    POJ3648
    求字典序最小的解
    */
    #include
    <iostream>
    #include
    <stdio.h>
    using namespace std;
    const int MAXN=20000;
    const int MAXM=100000;//这个必须开大一点
    struct Node
    {
    int a,b,pre,next;
    }E[MAXM],E2[MAXM];
    //原图和逆图
    int _n,n,m,V[MAXN],ST[MAXN][2],Q[MAXN],Q2[MAXN],vst[MAXN];
    bool res_ex;
    void init_d()//初始化
    {
    for(int i=0;i<n;i++)//相当于建出双重邻接表的头结点
    E[i].a=E[i].pre=E[i].next=E2[i].a=E2[i].pre=E2[i].next=i;
    m
    =n;//m是建造双重邻接表时结点的编号
    }
    void add_edge(int a,int b)//加入边(a,b),需要在原图和逆图中添加边
    {//实际上建造出的是循环状的图
    E[m].a=a;E[m].b=b;E[m].pre=E[a].pre;E[m].next=a;E[a].pre=m;E[E[m].pre].next=m;
    E2[m].a
    =b;E2[m].b=a;E2[m].pre=E2[b].pre;E2[m].next=b;E2[b].pre=m;E2[E2[m].pre].next=m++;
    }
    void solve()
    {
    for(int i=0;i<n;i++)
    {
    V[i]
    =0;
    vst[i]
    =0;
    }
    res_ex
    =1;
    int i,i1,i2,j,k,len,front,rear,front2,rear2;
    bool ff;
    for(int _i=0;_i<_n;_i++)
    {
    if(V[_i<<1]==1||V[(_i<<1)+1]==1) continue;//找一对未被确定的点
    i=_i<<1;len=0;
    if(!V[i])
    {
    ST[len][
    0]=i;ST[len++][1]=1;
    if(!V[i ^ 1])
    {
    ST[len][
    0]=i^1;
    ST[len
    ++][1]=2;
    }
    Q[front
    =rear=0]=i;
    vst[i]
    =i1=n+i;
    Q2[front2
    =rear2=0]=i^1;
    vst[i
    ^1]=i2=(n<<1)+i;
    //i1,i2为标志量,这样设置标志量使得每次都不一样,省去了初始化
    ff=1;
    for(;front<=rear;front++)
    {
    j
    =Q[front];
    for(int p=E[j].next;p!=j;p=E[p].next)
    {
    k
    =E[p].b;
    if(V[k]==2||vst[k]==i2||V[k^1]==1||vst[k^1]==i1)
    {ff
    =0;break;}
    if(vst[k]!=i1)
    {
    Q[
    ++rear]=k;vst[k]=i1;
    if(!V[k])
    {
    ST[len][
    0]=k;
    ST[len
    ++][1]=1;
    }
    }
    if(vst[k^1]!=i2)
    {
    Q2[
    ++rear2]=k^1;vst[k^1]=i2;
    if(!V[k])
    {
    ST[len][
    0]=k^1;
    ST[len
    ++][1]=2;
    }
    }
    }
    if(!ff) break;
    }
    if(ff)
    {
    for(;front2<=rear2;front2++)
    {
    j
    =Q2[front2];
    for(int p=E2[j].next;p!=j;p=E2[p].next)
    {
    k
    =E2[p].b;
    if(V[k]==1||vst[k]==i1)
    {
    ff
    =0;
    break;
    }
    if(vst[k]!=i2)
    {
    vst[k]
    =i2;Q2[++rear]=k;
    if(!V[k])
    {
    ST[len][
    0]=k;
    ST[len
    ++][1]=2;
    }
    }
    }
    if(!ff) break;
    }
    if(ff)
    {
    for(int j=0;j<len;j++) V[ST[j][0]]=ST[j][1];
    continue;
    }
    }
    }
    i
    =(_i<<1)+1;len=0;
    if(!V[i])
    {
    ST[len][
    0]=i;ST[len++][1]=1;
    if(!V[i ^ 1])
    {
    ST[len][
    0]=i^1;
    ST[len
    ++][1]=2;
    }
    Q[front
    =rear=0]=i;
    vst[i]
    =i1=n+i;
    Q2[front2
    =rear2=0]=i^1;
    vst[i
    ^1]=i2=(n<<1)+i;
    ff
    =1;
    for(;front<=rear;front++)
    {
    j
    =Q[front];
    for(int p=E[j].next;p!=j;p=E[p].next)
    {
    k
    =E[p].b;
    if(V[k]==2||vst[k]==i2||V[k^1]==1||vst[k^1]==i1)
    {ff
    =0;break;}
    if(vst[k]!=i1)
    {
    Q[
    ++rear]=k;vst[k]=i1;
    if(!V[k])
    {
    ST[len][
    0]=k;
    ST[len
    ++][1]=1;
    }
    }
    if(vst[k^1]!=i2)
    {
    Q2[
    ++rear2]=k^1;vst[k^1]=i2;
    if(!V[k])
    {
    ST[len][
    0]=k^1;
    ST[len
    ++][1]=2;
    }
    }
    }
    if(!ff) break;
    }
    if(ff)
    {
    for(;front2<=rear2;front2++)
    {
    j
    =Q2[front2];
    for(int p=E2[j].next;p!=j;p=E2[p].next)
    {
    k
    =E2[p].b;
    if(V[k]==1||vst[k]==i1)
    {
    ff
    =0;
    break;
    }
    if(vst[k]!=i2)
    {
    vst[k]
    =i2;Q2[++rear]=k;
    if(!V[k])
    {
    ST[len][
    0]=k;
    ST[len
    ++][1]=2;
    }
    }
    }
    if(!ff) break;
    }
    if(ff)
    {
    for(int j=0;j<len;j++) V[ST[j][0]]=ST[j][1];
    continue;
    }
    }
    }
    if(V[_i<<1]+V[(_i<<1)+1]!=3){res_ex=0;break;}
    }
    }
    int main()
    {
    int _m,a,b;
    char ch1,ch2;
    while(scanf("%d%d",&_n,&_m)!=EOF)//_n是点的对数,_m是冲突的点个数
    {
    if(_n==0&&_m==0)break;
    n
    =_n<<1;
    init_d();
    for(int i=0;i<_m;i++)
    {
    scanf(
    "%d%c%d%c",&a,&ch1,&b,&ch2);
    if(ch1=='w') a=2*a;
    else a=2*a+1;
    if(ch2=='w') b=2*b;
    else b=2*b+1;
    if(a!=(b^1))
    {
    add_edge(a,b
    ^1);//选a,必选b^1,
    add_edge(b,a^1);//选b,必选a^1,
    }
    }
    add_edge(
    0,1);//加一条新娘到新郎的边。
    //表示选了新娘必选新郎,这样如果选了新娘就会判断无解。
    //这样选出来的组合必定是有新郎的,即和新郎坐在同一侧的
    solve();
    bool first=false;
    if(res_ex)
    {
    for(int i=0;i<n;i++)
    {
    if(V[i]==1&&i!=1)
    {
    if(first)printf(" ");
    else first=true;
    printf(
    "%d%c",i/2,i%2==0?'h':'w');//选取的是和新郎同一侧的,输出和新娘同一侧的
    //所以输出的时候h和w换一下
    }
    }
    printf(
    "\n");
    }
    else printf("bad luck\n");
    }
    return 0;
    }
  • 相关阅读:
    数据库第三范式的思考
    channel通道例子
    go 测试代码性能实例
    go 新建項目引入gin失敗
    go 创建切片slice的四种方法
    Hibernate查询操作
    shell 分割训练数据
    hadoop streaming 分桶到不同的part
    C语言调用另一个文件的方法
    在springboot中使用jdbcTemplate(3)
  • 原文地址:https://www.cnblogs.com/kuangbin/p/2149667.html
Copyright © 2020-2023  润新知