《受欢迎的奶牛》和强连通分支Tarjan算法

本文介绍了一种用于计算给定条件下绝对奶牛数量的算法。通过输入描述的奶牛间相互喜欢的关系,该算法能够高效地确定满足条件的绝对奶牛。详细解释了算法的实现过程,包括使用并查集数据结构进行强连通分量的查找,并提供了代码示例。最终输出结果为绝对奶牛的数量。
摘要由CSDN通过智能技术生成

题目描述:

有一些奶牛,其中某些奶牛受其他奶牛欢迎。每头奶牛都喜欢自己,同时都追随它喜欢奶牛的爱好。如果A喜欢B,而B喜欢C,那么A也会喜欢C。如果所有人都喜欢某头奶牛,那么这头奶牛就称之为绝对奶牛。

现在有N头奶牛,同时告诉你M条A喜欢B的信息,请你计算出有多少头绝对奶牛。

输入描述:

输入文件第一行是N和M。此后M行,每行两个整数A,B(1<=A,B<=N),表示奶牛B受奶牛A欢迎,也就是奶牛A喜欢奶牛B。

输出描述:

输出文件仅有一行,是绝对奶牛的数目。

输入样例:

3 3
1 2
2 1
2 3

输出样例:

1

数据范围:

对于30%的数据,有1<=N<=1000,1<=M<=5000;

对于100%的数据,有1<=N<=100000,1<=M<=500000。

-----------------------------------------------------------------

Tarjan求强连通分支

代码如下

#include <cstdio>
#include <algorithm>
struct Edge {
    int from, to;
    Edge* next;
};
const int MAXN = 100001, MAXM = 500001;
struct UFS {
    int father[MAXN], rank[MAXN], root(int);
    void initialize(int), plus(int, int);
};
int n, m, low[MAXN], dfn[MAXN], nowdfn, stk[MAXN], top, s[MAXN];
Edge* head[MAXN];
bool vis[MAXN], ins[MAXN];
Edge edges[MAXM];
UFS ufs;
void dfs(int);
int main() {
    int cnt = 0, tmp;
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= m; i++) {
        int s, t;
        Edge* tmp = new Edge;
        scanf("%d%d", &s, &t);
        tmp->from = s;
        tmp->to = t;
        tmp->next = head[s];
        head[s] = tmp;
    }
    ufs.initialize(n);
    for(int i = 1; i <= n; i++) {
        if(!vis[i]) {
            dfs(i);
        }
    }
    for(int i = 1; i <= n; i++) {
        for(Edge* j = head[i]; j != NULL; j = j->next) {
            if(ufs.root(i) != ufs.root(j->to)) {
                s[ufs.root(i)]++;
            }
        }
    }
    for(int i = 1; i <= n; i++) {
        if(ufs.root(i) == i && s[i] == 0) {
            cnt++;
            tmp = i;
        }
    }
    printf("%d\n", cnt == 1 ? ufs.rank[tmp] : 0);
    return 0;
}
void UFS::initialize(int n) {
    for(int i = 1; i <= n; i++) {
        father[i] = i;
        rank[i] = 1;
    }
}
int UFS::root(int x) {
    return father[x] == x ? x : root(father[x]);
}
void UFS::plus(int x, int y) {
    father[y] = x;
    rank[x] += rank[y];
}
void dfs(int x) {
    nowdfn++;
    top++;
    dfn[x] = low[x] = nowdfn;
    ins[x] = vis[x] = true;
    stk[top] = x;
    for(Edge* i = head[x]; i != NULL; i = i->next) {
        int go = i->to;
        if(!vis[go]) {
            dfs(go);
            low[x] = std::min(low[x], low[go]);
        }
        if(ins[go]) {
            low[x] = std::min(low[x], dfn[go]);
        }
    }
    if(dfn[x] == low[x]) {
        int f = stk[top];
        top--;
        if(f != x) {
            while(true) {
                int i = stk[top];
                top--;
                ufs.plus(f, i);
                if(i == x) {
                    break;
                }
            }
        }
    }
}

但是会爆递归栈只有70分

Egg Pain的我就Egg Pain地写了一个Egg Pain的递归栈模拟,特此纪念,看代码。!

#include <cstdio>
#include <algorithm>
struct Edge {
    int from, to;
    Edge* next;
};
const int MAXN = 100001, MAXM = 500001;
struct UFS {
    int father[MAXN], rank[MAXN], root(int);
    void initialize(int), plus(int, int);
    ~UFS();
};
struct Cell {
    int x;
    bool finished, first, visjto;
    Edge* j;
    void call();
};
int n, m, low[MAXN], dfn[MAXN], nowdfn, stk[MAXN], top, s[MAXN], cst;
bool vis[MAXN], ins[MAXN];
Edge* head[MAXN];
UFS ufs;
Cell cs[MAXN];
int main() {
    int cnt = 0, tmp;
    freopen("popular.in", "r", stdin);
    freopen("popular.out", "w", stdout);
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= m; i++) {
        int s, t;
        Edge* tmp = new Edge;
        scanf("%d%d", &s, &t);
        tmp->from = s;
        tmp->to = t;
        tmp->next = head[s];
        head[s] = tmp;
    }
    ufs.initialize(n);
    for(int i = 1; i <= n; i++) {
        if(!vis[i]) {
            cst = 1;
            cs[cst].x = i;
            cs[cst].first = true;
            cs[cst].finished = false;
            while(cst != 0) {
                cs[cst].call();
                if(cs[cst].finished) {
                    cst--;
                }
            }
        }
    }
    for(int i = 1; i <= n; i++) {
        for(Edge* j = head[i]; j != NULL; j = j->next) {
            if(ufs.root(i) != ufs.root(j->to)) {
                s[ufs.root(i)]++;
            }
        }
    }
    for(int i = 1; i <= n; i++) {
        if(ufs.root(i) == i && s[i] == 0) {
            cnt++;
            tmp = i;
        }
    }
    printf("%d\n", cnt == 1 ? ufs.rank[tmp] : 0);
    for(int i = 1; i <= n; i++) {
        while(head[i] != NULL) {
            Edge* tmp = head[i]->next;
            delete head[i];
            head[i] = tmp;
        }
    }
    fclose(stdin);
    fclose(stdout);
    return 0;
}
void UFS::initialize(int n) {
    for(int i = 1; i <= n; i++) {
        father[i] = i;
        rank[i] = 1;
    }
}
int UFS::root(int x) {
    return father[x] == x ? x : root(father[x]);
}
void UFS::plus(int x, int y) {
    father[y] = x;
    rank[x] += rank[y];
}
UFS::~UFS() {
    delete[] father;
    delete[] rank;
}
void Cell::call() {
    if(first) {
        nowdfn++;
        top++;
        dfn[x] = low[x] = nowdfn;
        ins[x] = vis[x] = true;
        stk[top] = x;
        j = head[x];
        first = false;
    }
    else {
        if(!visjto) {
            low[x] = std::min(low[x], low[j->to]);
        }
        if(ins[j->to]) {
            low[x] = std::min(low[x], dfn[j->to]);
        }
        j = j->next;
    }
    if(j == NULL) {
        if(low[x] == dfn[x]) {
            int f = stk[top];
            top--;
            if(f != x) {
                while(true) {
                    int i = stk[top];
                    top--;
                    ufs.plus(f, i);
                    if(i == x) {
                        break;
                    }
                }
            }
        }
        finished = true;
    }
    else {
        if(!vis[j->to]) {
            cst++;
            cs[cst].x = j->to;
            cs[cst].first = true;
            cs[cst].finished = false;
            visjto = false;
        }
        else {
            visjto = true;
        }
    }
}

转载于:https://www.cnblogs.com/nealchen/p/4237526.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值