POJ 1291: This Sentence is False

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


题意:

给出n句话,

形如“第x句话是错误的”或“第x句话是正确的”。

判断是否有合法结果。

若有,计算至多有多少句话是正确的。


算法:

典型的并查集。

对于每个节点,维护与父节点的距离d[x],

同一并查集内,

相对距离为偶数的节点共真假。


每处理一条关系,

若这两句话不在一个集合内,

则通过这两句话间的相对距离(若同奇偶即为0,不同奇偶即为1)计算两根节点间的相对距离,然后合并。

若这两句话在一个集合内,

则判断两节点目前的相对距离是否与这条关系矛盾,

若矛盾即无结果。


计算结果时,

对于同一个集合中,d[x]为奇的划为一类,为偶的划为一类,取每个集合中数量较多的一类相加就可以。


——————————以下是废话的分界线——————————


本题实际是一个标准的2-SAT模型。

并查集的做法,相当于建立一个无向图模型。

对于一句话,

若形如“第x句话是错误的”,则在两点间建一条长度为1的边,

若形如“第x句话是正确的”,则在两点间建一条长度为0的边。

无向图模型中的一条边相当于2-SAT模型中的4条边。

在实际计算中,距离的具体长度是我们所不关心的,

距离的奇偶性才是我们应该关心的。

所以可以将距离对2取余。

实际上每个联通块,被缩成了两个点,这两个点中的句子不同真假。

也就是说,若只考虑长度为奇数的边,那么每一个联通块都构成了一个二部图。

至此,并查集的做法与2-SAT模型的联系就体现了出来。

显然,若图中存在奇圈,则无解。

因为奇圈相当于,两点间存在两条长度奇偶性不同的路线。

所以必定不存在合法方案。


代码如下:

int变量版:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<sstream>
#include<cstdlib>
#include<cstring>
#include<string>
#include<climits>
#include<cmath>
#include<queue>
#include<vector>
#include<stack>
#include<set>
#include<map>
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;

const int MAXN=1100;
int p[MAXN],d[MAXN],cot[2][MAXN];

int findp(int x) {
    if(p[x]==-1) {
        return x;
    }
    int ret=findp(p[x]);
    d[x]=(d[x]+d[p[x]])&1;
    return p[x]=ret;
}

int main() {
    int n;
    while(scanf("%d",&n),n) {
        memset(p,-1,sizeof(p));
        memset(d,0,sizeof(d));
        bool flg=true;
        for(int x=0; x<n; x++) {
            int y;
            char s[100];
            scanf("%s%d%s%s",s,&y,s,s);
            y--;
            int px=findp(x);
            int py=findp(y);
            int ret;
            if(s[0]=='t') {
                ret=0;
            } else {
                ret=1;
            }
            if(px!=py) {
                d[py]=(d[x]+d[y]+ret)&1;
                p[py]=px;
            } else {
                if(((d[x]+d[y])&1)!=ret) {
                    flg=false;
                }
            }
        }
        if(!flg) {
            puts("Inconsistent");
        } else {
            int ans=0;
            memset(cot,0,sizeof(cot));
            for(int i=0; i<n; i++) {
                int px=findp(i);
                cot[d[i]&1][px]++;
            }
            for(int i=0; i<n; i++) {
                ans+=max(cot[0][i],cot[1][i]);
            }
            printf("%d\n",ans);
        }
    }
    return 0;
}

bool变量版:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<sstream>
#include<cstdlib>
#include<cstring>
#include<string>
#include<climits>
#include<cmath>
#include<queue>
#include<vector>
#include<stack>
#include<set>
#include<map>
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;

const int MAXN=1100;
int p[MAXN],cot[2][MAXN];
bool d[MAXN];

int findp(int x) {
    if(p[x]==-1) {
        return x;
    }
    int ret=findp(p[x]);
    d[x]^=d[p[x]];
    return p[x]=ret;
}

int main() {
    int n;
    while(scanf("%d",&n),n) {
        memset(p,-1,sizeof(p));
        memset(d,0,sizeof(d));
        bool flg=true;
        for(int x=0; x<n; x++) {
            int y;
            char s[100];
            scanf("%s%d%s%s",s,&y,s,s);
            y--;
            int px=findp(x);
            int py=findp(y);
            bool ret;
            if(s[0]=='t') {
                ret=false;
            } else {
                ret=true;
            }
            if(px!=py) {
                d[py]=d[x]^d[y]^ret;
                p[py]=px;
            } else {
                if(d[x]^d[y]^ret) {
                    flg=false;
                }
            }
        }
        if(!flg) {
            puts("Inconsistent");
        } else {
            int ans=0;
            memset(cot,0,sizeof(cot));
            for(int i=0; i<n; i++) {
                int px=findp(i);
                cot[d[i]?1:0][px]++;
            }
            for(int i=0; i<n; i++) {
                ans+=max(cot[0][i],cot[1][i]);
            }
            printf("%d\n",ans);
        }
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值