hdoj 1824 Let's go home(2-SAT)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1824

思路分析:该问题为2-SAT问题;需要注意逻辑推理的等价性;

(1)题目第一个条件:每一个队或者队长留下或者其与两名队员同时留下,或者表明只能为两种情况中的一种;假设三人为A,B,C,队长为A,0表示不留下,1表示留下,因为B与C同时留下或者不留下,只要B,C中其中一个没有留下或者留下,则B,C中另一个也同样留下或者不留下,所以可以从该条件中推导出六条等价关系,即A不留下->B,C同时留下,A留下->B,C同时不留下,B留下->C留下,A不留下,B留下->C留下,A不留下,C留下->B留下,A不留西,C不留下->B不留下,A留下;

(2)题目中第二个条件:每一对队员,如果队员A留下,则B必须回家休息,或者B留下,A必须回家休息;则可以推导出两条等价式:A留下->B不留下,B留下->A不留下,注意在这个条件中可以A,B都不留下;

 

代码如下:

#include <cstdio>

#include <vector>

#include <cstring>

#include <iostream>

using namespace std;



const int MAX_N = 2 * 5000 + 10;

struct TwoSAT {

    int n;

    vector<int> G[2 * MAX_N];

    bool mark[2 * MAX_N];

    int S[2 * MAX_N], c;



    void Init(int n)

    {

        this->n = n;

        for (int i = 0; i <= 2 * n; ++i)

            G[i].clear();

        memset(mark, 0, sizeof(mark));

    }



    bool Dfs(int x)

    {

        if (mark[x ^ 1])  return false;

        if (mark[x])      return true;

        mark[x] = true;

        S[c++] = x;



        for (int i = 0; i < G[x].size(); ++i)

        {

            if (!Dfs(G[x][i]))

                return false;

        }

        return true;

    }



    void AddClause(int x, int y)

    {

        int a = 2 * x;

        int b = 2 * y;

        G[a ^ 1].push_back(b);

        G[b ^ 1].push_back(a);

    }



    void AddClauseTeam(int i, int j, int k)

    {

        int a = 2 * i;

        int b = 2 * j;

        int c = 2 * k;

        G[a].push_back(b ^ 1);

        G[a].push_back(c ^ 1);

        G[b].push_back(a ^ 1);

        G[b].push_back(c);

        G[c].push_back(a ^ 1);

        G[c].push_back(b);

        G[a ^ 1].push_back(b);

        G[a ^ 1].push_back(c);

        G[b ^ 1].push_back(a);

        G[b ^ 1].push_back(c ^ 1);

        G[c ^ 1].push_back(a);

        G[c ^ 1].push_back(b ^ 1);

    }



    bool Solve()

    {

        for (int i = 0; i < 2 * n; i += 2)

        {

            if (!mark[i] && !mark[i + 1])

            {

                c = 0;

                if (!Dfs(i))

                {

                    while (c > 0) mark[S[--c]] = false;

                    if (!Dfs(i + 1))

                        return false;

                }

            }

        }

        return true;

    }

};



TwoSAT sat;

int main()

{

    int n, m;



    while (scanf("%d %d", &n, &m) != EOF)

    {

        int a, b, c;

        sat.Init(3 * n);

        for (int i = 0; i < n; ++i)

        {

            scanf("%d %d %d", &a, &b, &c);

            sat.AddClauseTeam(a, b, c);

        }

        for (int i = 0; i < m; ++i)

        {

            scanf("%d %d", &a, &b);

            sat.AddClause(a, b);

        }



        bool ok = sat.Solve();

        if (ok)

            printf("yes\n");

        else

            printf("no\n");

    }

    return 0;

}

你可能感兴趣的:(home)