SPOJ PARADOX Paradox bfs + 缩点

题目:

http://www.spoj.com/problems/PARADOX/

题意:

有n个人,每个人都说某个人说的话是真的或者假的,可以说自己,问给出的这些语句矛不矛盾?

思路:

首先,建图时肯定要建有向图,边有值:真或假。用tarjan给每个点标出其所在的环,然后以环中的某个点为起点,随意设其状态真或假(我全部标为真),然后bfs给同一个环中的点标号,bfs过程中遇到不在同一个环的点,直接跳过即可,也就是说决定是否发生矛盾的是同一个环中的点。对于不同环中的点直接跳过的原因,例如m个点组成一个有向环,最少有m条边,两个环之间明显没有边(此处的环中的点数大于1),否则边数大于点数,和题意不符,那么也就是说两个环中必定有一个是单点,这样的话,无论是单点环指向多点环还是多点环指向单点环,都可以从容的给单点环标号,不会发生任何矛盾。

#include <bits/stdc++.h>

using namespace std;

const int N = 110;

struct edge
{
    int to, f, next;
}g[N*N*2];

int cnt, head[N];
int f[N];
int scc[N], dfn[N], low[N], scc_sz[N], st[N];
int idx, num, top;
bool vis[N];
void init()
{
    cnt = 0;
    memset(head, -1, sizeof head);
    memset(vis, 0, sizeof vis);
    memset(dfn, -1, sizeof dfn);
    memset(low, -1, sizeof low);
    memset(scc, 0, sizeof scc);
    memset(scc_sz, 0, sizeof scc_sz);
    idx = top = num = 0;
}
void add_edge(int v, int u, int f)
{
    g[cnt].to = u, g[cnt].f = f, g[cnt].next = head[v], head[v] = cnt++;
}
void tarjan(int v)
{
    dfn[v] = low[v] = idx++;
    st[top++] = v, vis[v] = true;
    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, scc_sz[num]++;
        }while(u != v);
    }
}
bool bfs(int s, int flag)
{
    queue<int> que;
    que.push(s), f[s] = flag;
    while(! que.empty())
    {
        int v = que.front(); que.pop();
        for(int i = head[v]; i != -1; i = g[i].next)
        {
            int u = g[i].to;
            if(scc[u] != scc[v]) continue;
            if(f[u] == -1)
            {
                f[u] = (f[v] ? g[i].f : !g[i].f);
                que.push(u);
            }
            else
            {
                if(f[u] != (f[v] ? g[i].f : !g[i].f)) return false;
            }
        }
    }
    return true;
}
int main()
{
    int n, v;
    char s[10];
    while(scanf("%d", &n), n)
    {
        init();
        for(int i = 1; i <= n; i++)
        {
            scanf("%d%s", &v, s);
            add_edge(i, v, s[0] == 't');
        }
        for(int i = 1; i <= n; i++)
            if(dfn[i] == -1) tarjan(i);
        memset(f, -1, sizeof f);
        bool flag = true;
        for(int i = 1; i <= n; i++)
            if(f[i] == -1)
            {
                flag = bfs(i, 1);
                if(! flag) break;
            }
        puts(flag ? "NOT PARADOX" : "PARADOX");
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值