现在的位置: 首页 > 综合 > 正文

hdu4115 Eliminate the Conflict(3SAT->2SAT)

2017年12月13日 ⁄ 综合 ⁄ 共 5570字 ⁄ 字号 评论关闭

Eliminate the Conflict

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 982    Accepted Submission(s): 410


Problem Description
Conflicts are everywhere in the world, from the young to the elderly, from families to countries. Conflicts cause quarrels, fights or even wars. How wonderful the world will be if all conflicts can be eliminated.
Edward contributes his lifetime to invent a 'Conflict Resolution Terminal' and he has finally succeeded. This magic item has the ability to eliminate all the conflicts. It works like this:
If any two people have conflict, they should simply put their hands into the 'Conflict Resolution Terminal' (which is simply a plastic tube). Then they play 'Rock, Paper and Scissors' in it. After they have decided what they will play, the tube should be opened
and no one will have the chance to change. Finally, the winner have the right to rule and the loser should obey it. Conflict Eliminated!
But the game is not that fair, because people may be following some patterns when they play, and if the pattern is founded by others, the others will win definitely.
Alice and Bob always have conflicts with each other so they use the 'Conflict Resolution Terminal' a lot. Sadly for Bob, Alice found his pattern and can predict how Bob plays precisely. She is very kind that doesn't want to take advantage of that. So she tells
Bob about it and they come up with a new way of eliminate the conflict:
They will play the 'Rock, Paper and Scissors' for N round. Bob will set up some restricts on Alice.
But the restrict can only be in the form of "you must play the same (or different) on the ith and jth rounds". If Alice loses in any round or break any of the rules she loses, otherwise she wins.
Will Alice have a chance to win?
 


Input
The first line contains an integer T(1 <= T <= 50), indicating the number of test cases.
Each test case contains several lines.
The first line contains two integers N,M(1 <= N <= 10000, 1 <= M <= 10000), representing how many round they will play and how many restricts are there for Alice.
The next line contains N integers B1,B2, ...,BN, where Bi represents what item Bob will play in the ith round. 1 represents Rock, 2 represents Paper, 3 represents Scissors.
The following M lines each contains three integers A,B,K(1 <= A,B <= N,K = 0 or 1) represent a restrict for Alice. If K equals 0, Alice must play the same on Ath and Bth round. If K equals 1, she must play different items on Ath and Bthround.
 


Output
For each test case in the input, print one line: "Case #X: Y", where X is the test case number (starting with 1) and Y is "yes" or "no" represents whether Alice has a chance to win.
 


Sample Input
2 3 3 1 1 1 1 2 1 1 3 1 2 3 1 5 5 1 2 3 2 1 1 2 1 1 3 1 1 4 1 1 5 1 2 3 0
 


Sample Output
Case #1: no Case #2: yes
Hint
'Rock, Paper and Scissors' is a game which played by two person. They should play Rock, Paper or Scissors by their hands at the same time. Rock defeats scissors, scissors defeats paper and paper defeats rock. If two people play the same item, the game is tied..
 


Source

题目大意:ACM两位神牛Alice和Bob又来玩游戏了,这次他们玩石头剪刀布。规定石头、布、剪刀分别用1、2、3表示。他们准备玩n局,现在Alice已知Bob的n局的出法,但是为了公平,对于Alice,Bob给了m个限制,每个限制3个参数:a  b  k。k=0或1。当k = 0的时候,表示第a局和第j局Alice必须出相同的手势,k= 1表示Alice在第a局和第j局必须出不同的手势。如果n轮比完,Alice一局没输的话,Alice赢,否则Bob赢。求Alice是否有赢的可能。

题目分析:因为每一轮有3种手势,乍一看是3-sat问题。但因为对于Alice来说,一局也不能输,所以只要n局保持不败就可以了,所以对于Alice来说,每一局其实只有2种情况,于是利用这个条件可以将此3-sat问题转化为2-sat问题。

先来看一张表:

Bob          Alice

                平      赢

1             1        2

2             2        3

3             3        1

一共就这6种情况。可以看出,无论Bob第a局和第b局出什么,Alice要保持不败,至少有一种手势可以在第a局和第b局同时做。所以我们先根据Bob第i局的手势,找出Alice第i局保持不败的2种手势,然后开始建图。

如果第a局和第b局Alice的手势要不同,那么矛盾的情况是Alice第a局和第b局手势相同,那么枚举这4种情况,找到矛盾的点对,建边,如果ab矛盾,建a->b',b->a'。

如果第a局和第b局Alice的手势要相同,那么矛盾的情况是Alice第a局和第b局手势不同,根据分析可知,无论第a局和第b局Bob出什么手势,Alice在这两局保持不败的策略中至少有一对是相同的,先考虑2对都相同的,即Bob在第a局和第b局手势相同,那么直接建边a->b,b->a,a + n->b + n,b + n->a + n(假设ab同,a+n  b+n同),一共4条边。如果没有2对相同,那么至少一对相同,依次枚举,找到那对相同的,只有这对相同的合理,其他的都不合理,那么仿照经典的And和Or操作中的建图,假设ab相同,那么只有ab是合法的,所以直接建a+n->a,b+n->b。

详情请见代码:

#include <iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 10001;
const int M = 10001;
struct edge
{
    int to,next;
}g[M<<1];
int head[N<<1];
int scc[N<<1];
int vis[N<<1];
int stack1[N<<1];
int stack2[N<<1];
int n,m,num;
int lcm[N][2];
bool flag;

void init()
{
    memset(head,-1,sizeof(head));
    memset(vis,0,sizeof(vis));
    memset(scc,0,sizeof(scc));
    stack1[0] = stack2[0] = num = 0;
    flag = true;
}

void build(int s,int e)
{
    g[num].to = e;
    g[num].next = head[s];
    head[s] = num ++;
}

void dfs(int cur,int &sig,int &cnt)
{
    if(!flag)
        return;
    vis[cur] = ++sig;
    stack1[++stack1[0]] = cur;
    stack2[++stack2[0]] = cur;
    for(int i = head[cur];~i;i = g[i].next)
    {
        if(!vis[g[i].to])
            dfs(g[i].to,sig,cnt);
        else
        {
            if(!scc[g[i].to])
            {
                while(vis[stack2[stack2[0]]] > vis[g[i].to])
                    stack2[0] --;
            }
        }
    }
    if(stack2[stack2[0]] == cur)
    {
        stack2[0] --;
        cnt ++;
        do
        {
            scc[stack1[stack1[0]]] = cnt;
            int tmp = stack1[stack1[0]];
            if((tmp > n && scc[tmp - n] == cnt) || (tmp <= n && scc[tmp + n] == cnt))
            {
                flag = false;
                return;
            }
        }while(stack1[stack1[0] --] != cur);
    }
}

void Gabow()
{
    int i,sig,cnt;
    sig = cnt = 0;
    for(i = 1;i <= n + n && flag;i ++)
        if(!vis[i])
            dfs(i,sig,cnt);
}

int nextint()
{
    char c;
    int ret;
    while((c = getchar()) > '9' || c < '0')
        ;
    ret = c - '0';
    while((c = getchar()) >= '0' && c <= '9')
        ret = ret * 10 + c - '0';
    return ret;
}

int main()
{
    int i,j,T,t,a,b;
    scanf("%d",&T);
    int cas = 0;
    while(T --)
    {
        scanf("%d%d",&n,&m);
        for(i = 1;i <= n;i ++)
        {
            scanf("%d",&t);
            lcm[i][0] = t;//tie
            lcm[i][1] = t + 1;//win
            if(lcm[i][1] > 3)
                lcm[i][1] = 1;
            if(lcm[i][0] > lcm[i][1])
                lcm[i][0] ^= lcm[i][1] ^= lcm[i][0] ^= lcm[i][1];
        }
        init();
        while(m --)
        {
            scanf("%d%d%d",&a,&b,&t);
            if(t)//different
            {//建图小心   找矛盾  
                if(lcm[a][0] == lcm[b][0])
                {
                    build(a,b + n);
                    build(b,a + n);
                }
                if(lcm[a][0] == lcm[b][1])
                {
                    build(a,b);
                    build(b + n,a + n);
                }
                if(lcm[a][1] == lcm[b][0])
                {
                    build(a + n,b + n);
                    build(b,a);
                }
                if(lcm[a][1] == lcm[b][1])
                {
                    build(a + n,b);
                    build(b + n,a);
                }
            }
            else//same
            {
                if(lcm[a][0] == lcm[b][0] && lcm[a][1] == lcm[b][1])
                {
                    build(a,b);
                    build(b,a);
                    build(a + n,b + n);
                    build(b + n,a + n);
                }
                else
                {//必有一对相同的,只能选这一对
                    if(lcm[a][0] == lcm[b][0])
                    {
                        build(a + n,a);
                        build(b + n,b);
                    }
                    if(lcm[a][0] == lcm[b][1])
                    {
                        build(a + n,a);
                        build(b,b + n);
                    }
                    if(lcm[a][1] == lcm[b][0])
                    {
                        build(a,a + n);
                        build(b + n,b);
                    }
                    if(lcm[a][1] == lcm[b][1])
                    {
                        build(a,a + n);
                        build(b,b + n);
                    }
                }
            }
        }
        Gabow();
        printf("Case #%d: ",++cas);
        if(flag)
            printf("yes\n");
        else
            printf("no\n");
    }
    return 0;
}
//0MS	556K

抱歉!评论已关闭.