POJ2186 Popular Cows 【强连通分量】+【Kosaraju】+【Tarjan】+【Garbow】

POJ2186 Popular Cows 【强连通分量】+【Kosaraju】+【Tarjan】+【Garbow】

大家好,又见面了,我是全栈君。

Popular Cows
Time Limit: 2000MS   Memory Limit: 65536K
Total Submissions: 23445   Accepted: 9605

Description

Every cow’s dream is to become the most popular cow in the herd. In a herd of N (1 <= N <= 10,000) cows, you are given up to M (1 <= M <= 50,000) ordered pairs of the form (A, B) that tell you that cow A thinks that cow B is popular. Since popularity is transitive, if A thinks B is popular and B thinks C is popular, then A will also think that C is 

popular, even if this is not explicitly specified by an ordered pair in the input. Your task is to compute the number of cows that are considered popular by every other cow. 

Input

* Line 1: Two space-separated integers, N and M 

* Lines 2..1+M: Two space-separated numbers A and B, meaning that A thinks B is popular. 

Output

* Line 1: A single integer that is the number of cows who are considered popular by every other cow. 

Sample Input

3 3
1 2
2 1
2 3

Sample Output

1

Hint

Cow 3 is the only cow of high popularity. 

Source

题意:能够转换成“给定一些有向路,求有多少个点能够由其余的随意点到达。

题解:第一道强连通分量的题,大致总结下Kosaraju算法:求强连通分量主要是为了简化图的构造,假设分量外的一个点能到达分量内的当中一个点,那么它必然能到达分量内的全部点,所以某种程度上。强连通分量能够简化成一个点。详细的求解过程是:1、随意选定一个点開始对原图进行深搜,记录每一个点离开时的时间(更确切的说是求每一个时间相应哪个点离开)。2、对原图的反图进行深搜,步骤一中最后离开的点最先開始深搜。每次将同一棵树中的点都哈希成同一个值。最后有多少棵树就有多少个强连通分量。

这题最后全部点都哈希完毕后实际上构成了一个DAG。假设新图中出度为0的点仅仅有一个那么有解,解为该出度为0的强连通分量中原来点的个数。若出度为0的点不止一个,那么无解,由于有两群牛互不崇拜,此时答案为0.在推断连通分量是否有出度时有个小技巧,就是在对反图DFS时若发现连接到的点已訪问且它的哈希值与当前訪问点的哈希值不同。那么这个被连接到的点相应的联通分量是有出度的。然后还需记录每一个连通分量的点数。

#include <stdio.h>
#include <string.h>
#define maxn 10002
#define maxm 50002

int head0[maxn], head1[maxn], id;
int count[maxn], num[maxn], hash[maxn];
struct Node{
    int t0, next0, t1, next1;
} E[maxm];
bool vis[maxn], out[maxn];

void addEdge(int u, int v)
{
    E[id].t0 = v; E[id].next0 = head0[u];
    head0[u] = id; E[id].t1 = u;
    E[id].next1 = head1[v]; head1[v] = id++;
}

void getMap(int n, int m)
{
    int i, u, v; id = 0;
    memset(head0, -1, sizeof(int) * (n + 1)); //save time
    memset(head1, -1, sizeof(int) * (n + 1));
    for(i = 0; i < m; ++i){
        scanf("%d%d", &u, &v);
        addEdge(u, v);
    }
}

void DFS0(int pos, int& sig)
{
    vis[pos] = 1; int i;
    for(i = head0[pos]; i != -1; i = E[i].next0){
        if(!vis[E[i].t0]) DFS0(E[i].t0, sig);
    }
    num[++sig] = pos;
}

void DFS1(int pos, int sig)
{
    vis[pos] = 1; hash[pos] = sig;
    int i; ++count[sig];
    for(i = head1[pos]; i != -1; i = E[i].next1){
        if(!vis[E[i].t1]) DFS1(E[i].t1, sig);
        else if(hash[E[i].t1] != hash[pos]) out[hash[E[i].t1]] = 1;
    }
}

void solve(int n) //Kosaraju
{
    int i, sig = 0, tmp = 0, ans;
    memset(vis, 0, sizeof(bool) * (n + 1));
    for(i = 1; i <= n; ++i)
        if(!vis[i]) DFS0(i, sig);
    memset(vis, 0, sizeof(bool) * (n + 1));
    memset(count, 0, sizeof(int) * (n + 1));
    memset(out, 0, sizeof(bool) * (n + 1));
    i = sig; sig = 0;
    for(; i; --i)
        if(!vis[num[i]]) DFS1(num[i], ++sig);
    for(i = 1; i <= sig; ++i)
        if(!out[i]) ++tmp, ans = count[i];
    //printf("sig%d\n", sig);
    if(tmp == 1) printf("%d\n", ans);
    else printf("0\n");
}

int main()
{
    int n, m;
    while(scanf("%d%d", &n, &m) == 2){
        getMap(n, m);
        solve(n);
    }
    return 0;
}

Tarjan解法:

#include <stdio.h>
#include <string.h>
#define maxn 10002
#define maxm 50002

int head[maxn], vis[maxn], id, id2, scc_num, sec;
int dfn[maxn], low[maxn], sta[maxn], count[maxn];
bool out[maxn];
struct Node{
    int to, next;
} E[maxm];

int min(int a, int b){
    return a < b ?

a : b;}void addEdge(int u, int v){ E[id].to = v; E[id].next = head[u]; head[u] = id++;}void getMap(int n, int m){ int i, u, v; id = 0; memset(head, -1, sizeof(int) * (n + 1)); memset(vis, 0, sizeof(int) * (n + 1)); memset(out, 0, sizeof(bool) * (n + 1)); memset(count, 0, sizeof(int) * (n + 1)); for(i = 0; i < m; ++i){ scanf("%d%d", &u, &v); addEdge(u, v); }}void DFS(int pos) //强连通分量必然是该树的子树{ dfn[pos] = low[pos] = ++sec; vis[pos] = 1; sta[id2++] = pos; int i, u, v; for(i = head[pos]; i != -1; i = E[i].next){ v = E[i].to; if(!vis[v]) DFS(v); if(vis[v] == 1) low[pos] = min(low[pos], low[v]); } if(dfn[pos] == low[pos]){ ++scc_num; do{ ++count[scc_num]; u = sta[--id2]; low[u] = scc_num; vis[u] = 2; } while(u != pos); }}void solve(int n) //Tarjan{ int i, j, ok = 0, ans; sec = id2 = scc_num = 0; for(i = 1; i <= n; ++i) if(!vis[i]) DFS(i); for(i = 1; i <= n; ++i) for(j = head[i]; j != -1; j = E[j].next) if(low[i] != low[E[j].to]){ out[low[i]] = 1; break; } for(i = 1; i <= scc_num; ++i) if(!out[i]){ if(++ok > 1) break; ans = count[i]; } if(ok != 1) printf("0\n"); else printf("%d\n", ans);}int main(){ int n, m; while(scanf("%d%d", &n, &m) == 2){ getMap(n, m); solve(n); } return 0;}

Garbow解法:与Tarjan思想同样,仅仅是实现方式略有不同,效率更高一些。

#include <stdio.h>
#include <string.h>
#define maxn 10002
#define maxm 50002
//sta2用以维护当前连通分量的根
int head[maxn], id, sta1[maxn], id1, sta2[maxn], id2;
int low[maxn], scc[maxn], sccNum, sec, count[maxn];
struct Node{
    int to, next;
} E[maxm];
bool out[maxn];

void addEdge(int u, int v)
{
    E[id].to = v; 
    E[id].next = head[u];
    head[u] = id++;
}

void getMap(int n, int m)
{
    int i, u, v; id = 0;
    memset(head, -1, sizeof(int) * (n + 1));
    for(i = 0; i < m; ++i){
        scanf("%d%d", &u, &v);
        addEdge(u, v);
    }
}

void Garbow(int pos)
{
    low[pos] = ++sec;
    sta1[id1++] = sta2[id2++] = pos;
    for(int i = head[pos]; i != -1; i = E[i].next){
        if(!low[E[i].to]) Garbow(E[i].to);
        else if(!scc[E[i].to]){
            while(low[sta2[id2-1]] > low[E[i].to]) --id2;
        }
    }
    if(pos == sta2[id2-1]){        
        int v; ++sccNum; --id2;
        do{
            v = sta1[--id1];
            scc[v] = sccNum;
            ++count[sccNum];
        } while(sta1[id1] != pos);
    }
}

void solve(int n)
{
    int i, j; id1 = id2 = sec = sccNum = 0;
    memset(low, 0, sizeof(int) * (n + 1));
    memset(scc, 0, sizeof(int) * (n + 1));
    memset(count, 0, sizeof(int) * (n + 1));
    memset(out, 0, sizeof(bool) * (n + 1));
    for(i = 1; i <= n; ++i)
        if(!low[i]) Garbow(i);
    for(i = 1; i <= n; ++i)
        for(j = head[i]; j != -1; j = E[j].next)
            if(scc[i] != scc[E[j].to]){
                out[scc[i]] = 1; break;
            }
    int tmp = 0, ans;
    for(i = 1; i <= sccNum; ++i)
        if(!out[i]){
            if(++tmp > 1){
                ans = 0; break;
            }
            ans = count[i];
        }
    printf("%d\n", ans);
}

int main()
{
    int n, m;
    while(scanf("%d%d", &n, &m) == 2){
        getMap(n, m);
        solve(n);
    }
    return 0;
}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/115896.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 此工作站和主域直接信任失败_主域间的信任关系失败

    此工作站和主域直接信任失败_主域间的信任关系失败故障现象:win7输入账户登录后,提示错误”此工作站和主域间的信任关系失败”,所有域用户无法登录,如图:解决方式:1.微软官方,退域重新加入域https://support.microsoft.com/en-us/kb/27710402.其他建议,“重置计算机账户”https://redmondmag.com/articles/201…

    2022年10月18日
    0
  • React Native技术篇—自定义Toast弹窗「建议收藏」

    React Native技术篇—自定义Toast弹窗「建议收藏」注意:未经允许不可私自转载,违者必究ReactNative官方文档:https://reactnative.cn/docs/getting-started/项目GitHub地址:https://github.com/zhouwei1994/nativeCase.git在写自定义Toast弹窗之前我们要先创建一个ReactNative第二视图层。创建教程:https://b…

    2022年9月25日
    0
  • 测试用例模板案例

    测试用例模板案例qq账号:用例编号 所属模块 用例标题 优先级 前置条件 输入数据 操作步骤 预期结果 实际结果 是否通过 测试人员 测试时间 qq_dl_001 登录 账号为六位自然数组成 高 输入账号和密码点击登录 账号:123456密码:abcd12 1:输入账号 2:输入密码 3:点击”登录” 1:账号被填充 2:密码被填充 3:登录成功,跳转主界面 1:账号被填充

    2022年7月17日
    17
  • 关于IntelliJ Idea搭建javaweb项目出现的Error filterStart错误解决

    关于IntelliJ Idea搭建javaweb项目出现的Error filterStart错误解决随着java行业的快速发展,我们用的开发工具也从eclipse转到了IntelliJIdea,我相信很多朋友在用idea的时候都出过各种问题,今天我要说的这个问题,也是我耗费了一天时间才找到问题的缘由。          因为公司需要,我用idea搭建了一个Struts2+mybatis+spring的项目,因为之前没怎么用过idea,所以搭建的时候遇到了很多问题。在pom文件里把需要的

    2022年7月11日
    48
  • UFT工具简介

    UFT工具简介UFT UFT是一种自动测试工具。使用UFT的目的是想用它来执行重复的自动化测试,主要是用于回归测试和测试同一软件的新版本。因此你在测试前要考虑好如何对应用程序进行测试,例如要测试哪些功能、操作步骤、输入数据和期望的输出数据等  基本功能UFT提供符合所有主要应用软件环境的功能测试和回归测试的自动化。采用关键字驱动的理念以简化测试用例的创建和维护。它让用户可以直接录制屏幕上的操作流

    2022年5月15日
    164
  • 慧荣SM2246EN开卡Toggle 8贴东芝闪存SSD失败解决方法[通俗易懂]

    慧荣SM2246EN开卡Toggle 8贴东芝闪存SSD失败解决方法[通俗易懂]如果你的固态硬盘是慧荣SM2246EN主控的,且闪存为8贴9D2H9E2H9DDJ9EDJ9EFK9DFK9DFL9EFL9CDJ等8Die4CE的,那么遇到开卡失败的话,可以尝试下面的方法来解决:打开慧荣SM2246EN量产工具文件夹中的FlashDB文件夹,找到里面的Flash.SET(闪存参数配置)文件,用记事本打开此文件后,把对应的闪存参数第46项数值(十六进制)适当降低1-6(十六进制)即可解决。比如下面以9DDJ为例:减少后,再次去开卡,应该就能过了,如果你也遇到

    2022年6月8日
    73

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号