HDU 1824 Let's go home 2-sat判断可行解

来源:互联网 发布:域名第四级地址 编辑:程序博客网 时间:2024/06/06 01:12

题目:

http://acm.hdu.edu.cn/showproblem.php?pid=1824

题意:

Problem Description
小时候,乡愁是一枚小小的邮票,我在这头,母亲在那头。
—— 余光中
集训是辛苦的,道路是坎坷的,休息还是必须的。经过一段时间的训练,lcy决定让大家回家放松一下,但是训练还是得照常进行,lcy想出了如下回家规定,每一个队(三人一队)或者队长留下或者其余两名队员同时留下;每一对队员,如果队员A留下,则队员B必须回家休息下,或者B留下,A回家。由于今年集训队人数突破往年同期最高记录,管理难度相当大,lcy也不知道自己的决定是否可行,所以这个难题就交给你了,呵呵,好处嘛~,免费**漂流一日。

Input
第一行有两个整数,T和M,1<=T<=1000表示队伍数,1<=M<=5000表示对数。
接下来有T行,每行三个整数,表示一个队的队员编号,第一个队员就是该队队长。
然后有M行,每行两个整数,表示一对队员的编号。
每个队员只属于一个队。队员编号从0开始。

Output
可行输出yes,否则输出no,以EOF为结束。

思路:

对于第一个条件,是要么队长留下,要么两个队员同时留下(只有一个队员留下时队长必须留下),抽象一下就是 A OR (B AND C),总结出这个后我是懵逼的,这三个布尔变量怎么搞?事实是有办法搞定的,我们建边(A’,B)(B’,A)(A’,C)(C’,A),这样建边后,A不在的话,BC必定都在,B和C有一个不在,则A必须在,这个问题就解决了。对于第二个条件,两个队员只能留下一个,这个就比较简单了,建边(A,B’)(B,A’),这样就保证两个人只有一个在。强连通缩点,如果A和A’在同一个环内,无解,否则有解

总结:

数组开小了,哇了一发。。。

#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#include <queue>using namespace std;const int N = 6010;struct edge{    int to, next;} g[N*10];int cnt, head[N], cnt1, head1[N];int dfn[N], low[N], scc[N], st[N], top, num, idx;bool vis[N];int n, m;void add_edge(int v, int u){    g[cnt].to = u, g[cnt].next = head[v], head[v] = cnt++;}void init(){    memset(head, -1, sizeof head);    memset(dfn, -1, sizeof dfn);    memset(vis, 0, sizeof vis);    top = num = idx = cnt = 0;}void tarjan(int v){    dfn[v] = low[v] = ++idx;    vis[v] = true, st[top++] = v;    int u;    for(int i = head[v]; i != -1; i = g[i].next)    {        u = g[i].to;        if(dfn[u] == -1)        {            tarjan(u);            low[v] = min(low[v], low[u]);        }        else if(vis[u])low[v] = min(low[v], dfn[u]);    }    if(dfn[v] == low[v])    {        num++;        do        {            u = st[--top];            vis[u] = false;            scc[u] = num;        }        while(u != v);    }}int main(){    while(~ scanf("%d%d", &n, &m))    {        init();        int v, u, w;        for(int i = 1; i <= n; i++)        {            scanf("%d%d%d", &v, &u, &w);            add_edge(v + n*3, u), add_edge(u + n*3, v);            add_edge(v + n*3, w), add_edge(w + n*3, v);        }        for(int i = 1; i <= m; i++)        {            scanf("%d%d", &v, &u);            add_edge(v, u + n*3), add_edge(u, v + n*3);        }        for(int i = 0; i < 2*3*n; i++)            if(dfn[i] == -1) tarjan(i);        bool flag = true;        for(int i = 0; i < 3*n; i++)            if(scc[i] == scc[i+3*n])            {                flag = false; break;            }        if(flag) printf("yes\n");        else printf("no\n");    }    return 0;}
0 0