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

题目:

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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值