URAL 1382 Game with Cards (2-SAT)

来源:互联网 发布:cmd建立网络路径 编辑:程序博客网 时间:2024/05/19 08:36

题目大意:

给出n行描述,每行的两句话都是一句话是对的,一句话是错的,要求输出最终的牌号和人的编号匹配的情况下,每个人说的话中是第一句对还是第二句对,题目保证一定有解


大致思路:

对于每个人说的话可以视作一个命题,我们将这些命题编号从1到n; 对于命题 xi, 有两种状态,一种是前一句话对,一种是后一句话对,在2-SAT算法中将这两种状态分别用结点编号为 2*i 和 2*i + 1 的点来表示

那么对于每个命题xi,由于2-SAT算法本身保证了两个结点不会同时被染色,也就不会出现矛盾的情况,那么对于两个不同的命题 xi 和 xj 就需要看是否存在矛盾来进行连边。


对于命题 xi 和 xj :

            2*i         2*i + 1

xi      i -> a[i]     b[i] -> c[i]

xj      j -> a[j]     b[j] -> c[j]

            2*j           2*j + 1


由于i != j 当 a[i] == a[j] 时,由于两者同时出现将会产生矛盾, 需要连边  2*i -> 2*j + 1    和  2*j  -> 2*i + 1  即如果 xi 为假(结点2*i被染色),那么 xj 为真即需要染色 2*i + 1,另外一个同理。

当 i == b[i] || a[i] == c[j] 时, 连边 2*i -> 2*j   和 2*j + 1 -> 2*i + 1

当b[i] == j || c[i] == a[j] 时, 连边 2*i + 1 -> 2*j + 1 和 2*j -> 2*i

当 b[i] == b[j] || c[i] == c[j] 时, 连边 2*i + 1 -> 2*j 和 2*j + 1 -> 2*i

然后用2-SAT算法跑一遍就可以了

这样子对于命题 xi 如果结点 2*i 被标记就说明第一句话为真,否则说明第二句话为真

代码如下:

Result  :  Accepted     Memory  :  7941 KB     Time  :  46 ms

/* * Author: Gatevin * Created Time:  2014/7/23 19:28:14 * File Name: test.cpp */#include<iostream>#include<sstream>#include<fstream>#include<vector>#include<list>#include<deque>#include<queue>#include<stack>#include<map>#include<set>#include<bitset>#include<algorithm>#include<cstdio>#include<cstdlib>#include<cstring>#include<cctype>#include<cmath>#include<ctime>#include<iomanip>using namespace std;const double eps(1e-8);typedef long long lint;#define maxn 1010int n;int a[maxn], b[maxn], c[maxn];struct TwoSAT{    int n;    vector <int> G[maxn*2];    bool mark[maxn*2];    int S[maxn*2], c;        bool dfs(int x)    {        if(mark[x^1]) return false;//结点x的对立点已经标为成立        if(mark[x]) return true;//x已经被访问过,说明继续下去已经可以保证是对的了,不需要重复标记        mark[x] = true;        S[c++] = x;        for(unsigned int i = 0; i < G[x].size(); i++)            if(!dfs(G[x][i])) return false;//递推x结点能得到的结果,进行标记        return true;    }        void init(int n)    {        this->n = n;        for(int i = 0; i < n*2; i++)        {            G[i].clear();        }        memset(mark, 0, sizeof(mark));    }        //x = xval or y = yval    void add_clause(int x, int xval, int y, int yval)    {        x = x*2 + xval;        y = y*2 + yval;        G[x^1].push_back(y);        G[y^1].push_back(x);    }        bool solve()    {        for(int i = 0; i < n*2; i += 2)        {            if(!mark[i] && !mark[i + 1])            {                c = 0;                if(!dfs(i))                {                    while(c > 0) mark[S[--c]] = false;//dfs(i)如果失败就擦掉之前实验标记的点                    if(!dfs(i + 1)) return false;//换dfs(i = 1)上如果依旧失败说明无解                }            }        }        return true;    }};TwoSAT answer;int main(){    scanf("%d",&n);    for(int i = 1; i <= n; i++)    {        scanf("%d %d %d", &a[i], &b[i], &c[i]);    }    answer.init(n);    for(int i = 1; i <= n; i++)    {        for(int j = i + 1; j <= n; j++)        {            if(a[i] == a[j]) answer.add_clause(i - 1, 1, j - 1, 1);//对应的连边方式,这里编号需要减一,方便异或运算            if(i == b[j] || a[i] == c[j]) answer.add_clause(i - 1, 1, j - 1, 0);            if(j == b[i] || a[j] == c[i]) answer.add_clause(i - 1, 0, j - 1, 1);            if(b[i] == b[j] || c[i] == c[j]) answer.add_clause(i - 1, 0, j - 1, 0);        }    }    bool flag = answer.solve();    if(flag)    {        for(int i = 0; i < n*2; i += 2)        {            if(answer.mark[i]) printf("1");            else printf("2");            if(i != 2*n - 2) printf(" ");        }    }    return 0;}



0 0
原创粉丝点击