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

来源:互联网 发布:神仙道懒娃 源码 编辑:程序博客网 时间:2024/06/05 18:54

题目地址
题意:中文没什么好说的,就是有一点题意需要通过样例才知道,题目中的两名队员是可以包括队长的,只是当队长留下来的话,其他两个人可以留下也可以回家,并不是二选一一定要做什么,也可以全部留下,这是我之前没搞清楚的地方
思路:这道题让我更好的理解了2-SAT算法的建图以及解决方法,如果还不懂2-SAT的话可以看我另一篇博客:传送门,那里有资料和我对这个算法的一些理解。原来我是没有很好的理解建图的精髓。这题我就很好的理解了,他把每一个点进行拆点操作,把每个点拆成一个选点和一个不选点,然后经典的聚会问题(我上一篇博客)就是每一对夫妻只能选一个人去参加聚会,所以建边的时候就把夫妻之间连点,然后这题呢就是把队长与两个队员不去连上边表示那个限制条件,然后那些限制条件就按这个来建边(一个选与另一个不选的点进行建边),然后再进行缩点判环,看同一个点的留和走会不会在同一个联通分量中就好了,如果在的话就是不能按这样安排,反之就是可以的。

#include <iostream>#include <cstring>#include <string>#include <queue>#include <vector>#include <map>#include <set>#include <cmath>#include <cstdio>#include <algorithm>#include <iomanip>#define N 6010#define M 1000010//双倍#define LL __int64#define inf 0x3f3f3f3f#define lson l,mid,ans<<1#define rson mid+1,r,ans<<1|1#define getMid (l+r)>>1#define movel ans<<1#define mover ans<<1|1using namespace std;const LL mod = 1000000007;int n, m;int head[N], idx;struct node {    int to;    int next;}edge[M];struct two_SAT{    int dfn[N], low[N];    int stack[N], top;    int belong[N], cnt, num;    bool vis[N];    void init() {        memset(head, -1, sizeof(head));        memset(belong, 0, sizeof(belong));        memset(dfn, 0, sizeof(dfn));        memset(vis, false, sizeof(vis));        num = cnt = 0;        top = 0;        idx = 0;    }    void addedge(int a, int b) {        edge[idx].to = b;        edge[idx].next = head[a];        head[a] = idx++;    }    void tarjin(int u) {//缩点        dfn[u] = low[u] = ++num;        vis[u] = true;        stack[top++] = u;        for (int i = head[u]; ~i; i = edge[i].next) {            int v = edge[i].to;            if (!dfn[v]) {                tarjin(v);                low[u] = min(low[u], low[v]);            }            else if (vis[v]) {                low[u] = min(low[u], dfn[v]);            }        }        if (dfn[u] == low[u]) {            while (true) {                int v = stack[--top];                vis[v] = false;                belong[v] = cnt;//标记联通分量belong                 if (u == v) break;            }            cnt++;        }    }    bool two_sat() {        for (int i = 0; i < 6 * n; i++) {//总点数              if (!dfn[i]) tarjin(i);        }        for (int i = 0; i < 3 * n; i++) {//遍历是不是会有一个人有2种可能            if (belong[i] == belong[i + 3 * n]) {                return false;            }        }        return true;    }}sat;int main() {    cin.sync_with_stdio(false);    int a, b, c;    while (cin >> n >> m) {        sat.init();        for (int i = 0; i < n; i++) {            cin >> a >> b >> c;//i为留i+3*n为不留            sat.addedge(a + 3 * n, b); sat.addedge(a + 3 * n, c);            sat.addedge(b + 3 * n, a); sat.addedge(c + 3 * n, a);        }        for (int i = 0; i < m; i++) {            cin >> a >> b;            sat.addedge(a, b + 3 * n);            sat.addedge(b, a + 3 * n);        }        if (sat.two_sat()) {            cout << "yes" << endl;        }        else {            cout << "no" << endl;        }    }    return 0;}