zoukankan      html  css  js  c++  java
  • Tarjan缩点

    Tarjan缩点

    P3387 【模板】缩点

    思路

    既然时缩点的模板,那么缩点自然少不了了,缩点后我们的到新的有向无环图,然后再利用这个无环图去找一条最大权值的路径,路径和即为答案。

    我们改如何选取起点来避免不必要的计算,假设存在一条路径,我们的最大值一定时从起点开始的,所以我们选取所有的缩点以后入度为零的点去bfs,然后不断更新最大路径值。

    代码

    #include <bits/stdc++.h>
    
    using namespace std;
    
    typedef long long ll;
    
    inline ll read() {
        ll f = 1, x = 0;
        char c = getchar();
        while(c > '9' || c < '0') {
            if(c == '-')    f = -1;
            c = getchar();
        }
        while(c >= '0' && c <= '9') {
            x = (x << 1) + (x << 3) + (c ^ 48);
            c = getchar();
        }
        return f * x;
    }
    
    const int N1 = 1e4 + 10, N2 = 1e5 + 10;
    
    int head[N1], to[N2], nex[N2], cnt = 1;
    int dfn[N1], low[N1], visit[N1], scc[N1], point[N1], value[N1], n, m, tot, sum;
    int x[N2], y[N2];
    int stk[N1], top;
    
    int head1[N1], to1[N2], nex1[N2], cnt1 = 1;
    int in[N1];
    
    void tarjan(int rt) {
        dfn[rt] = low[rt] = ++tot;
        visit[rt] = 1;
        stk[++top] = rt;
        for(int i = head[rt]; i; i = nex[i]) {
            if(!dfn[to[i]]) {
                tarjan(to[i]);
                low[rt] = min(low[rt], low[to[i]]);
            }
            else if(visit[to[i]]) {
                low[rt] = min(low[rt], dfn[to[i]]);
            }
        }
        if(dfn[rt] == low[rt]) {
            sum++;
            do {
                visit[stk[top]] = 0;
                scc[stk[top]] = sum;
                value[sum] += point[stk[top]];
                top--;
            }while(stk[top + 1] != rt);
        }
    }
    
    void add(int x, int y) {
        to[cnt] = y;
        nex[cnt] = head[x];
        head[x] = cnt++;
    }
    
    void bfs() {
        queue<pair<int, int>> q;
        for(int i = 1; i <= sum; i++)
            if(in[i] == 0)
                q.push(make_pair(i, value[i]));
        int ans = 0;
        while(!q.empty()) {
            int temp = q.front().first;
            ans = max(ans, q.front().second);
            for(int i = head1[temp]; i; i = nex1[i])
                    q.push(make_pair(to1[i], q.front().second + value[to1[i]]));
            q.pop();
        }
        printf("%d
    ", ans);
    }
    
    int main() {
        // freopen("in.txt", "r", stdin);
        n = read(), m = read();
        for(int i = 1; i <= n; i++)
            point[i] = read();
        for(int i = 1; i <= m; i++) {
            x[i] = read(), y[i] = read();
            add(x[i], y[i]);
        }
        for(int i = 1; i <= n; i++)
            if(!dfn[i])
                tarjan(i);
        for(int i = 1; i <= m; i++)//缩点后重新建边。
            if(scc[x[i]] != scc[y[i]]) {
                in[scc[y[i]]]++;
                to1[cnt1] = scc[y[i]];
                nex1[cnt1] = head1[scc[x[i]]];
                head1[scc[x[i]]] = cnt1++;
            }
        bfs();
        return 0;
    }
    

    思路

    显然是一道缩点的题目,缩点完后,我们可以知道如果一个强连通分量的出度为零,并且只有一个强连通分量的初读为零,那么缩点后的图一定时联通的,这个时候出度为零的强连通分量重的点的个数就是我们要求的答案。

    代码

    // #include <bits/stdc++.h>
    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <stdlib.h>
    #include <queue>
    #include <stack>
    #include <vector>
    #include <bitset>
    
    using namespace std;
    
    typedef long long ll;
    
    inline ll read() {
        ll f = 1, x = 0;
        char c = getchar();
        while(c > '9' || c < '0') {
            if(c == '-')    f = -1;
            c = getchar();
        }
        while(c >= '0' && c <= '9') {
            x = (x << 1) + (x << 3) + (c ^ 48);
            c = getchar();
        }
        return f * x;
    }
    
    const int N1 = 1e4 + 10, N2 = 5e4 + 10;
    
    int head[N1], to[N2], nex[N2], cnt = 1;
    int visit[N1], dfn[N1], low[N1], scc[N1], sz[N1], n, m, tot, sum;
    int x[N2], y[N2], out[N1];
    int stk[N1], top;
    
    
    void add(int x, int y) {
        to[cnt] = y;
        nex[cnt] = head[x];
        head[x] = cnt++;
    }
    
    void tarjan(int rt) {
        visit[rt] = 1, stk[++top] = rt;
        dfn[rt] = low[rt] = ++tot;
        for(int i = head[rt]; i; i = nex[i]) {
            if(!dfn[to[i]]) {
                tarjan(to[i]);
                low[rt] = min(low[rt], low[to[i]]);
            }
            else if(visit[to[i]])
                low[rt] = min(low[rt], dfn[to[i]]);
        }
        if(dfn[rt] == low[rt]) {
            sum++;
            do {
                scc[stk[top]] = sum;
                sz[sum]++;
                visit[stk[top]] = 0;
                top--;
            }while(rt != stk[top + 1]);
        }
    }
    
    int main() {
        // freopen("in.txt", "r", stdin);
        while(scanf("%d %d", &n, &m) != EOF) {
            for(int i = 1; i <= n; i++)
                head[i] = visit[i] = sz[i] = out[i] = dfn[i] = 0;
            cnt = 1, tot = sum = top = 0;
            for(int i = 1; i <= m; i++) {
                x[i] = read(), y[i] = read();
                add(x[i], y[i]);
            }
            for(int i = 1; i <= n; i++)
                if(!dfn[i])
                    tarjan(i);
            // puts("okkkkk");
            for(int i = 1; i <= m; i++)
                if(scc[x[i]] != scc[y[i]])
                    out[scc[x[i]]]++;
            int num = 0, ans = 0;
            for(int i = 1; i <= sum; i++)
                if(out[i] == 0) {
                    ans = sz[i];
                    num++;
                }
            if(num != 1)    puts("0");
            else    printf("%d
    ", ans);
        }
        return 0;
    }
    

    Bomb

    思路

    容易想到爆炸就是一个传递的图,当爆炸形成一个环的时候,明显可以进行缩点操作,所以当我们进行完缩点之后,我们只要统计剩余的点中入度为零的点就行,同时我们需要的花费就是这些点所在的联通分量中的花费最小的点。

    代码

    #include <bits/stdc++.h>
    
    using namespace std;
    
    
    typedef long long ll;
    
    ll read() {
        ll f = 1, x = 0;
        char c = getchar();
        while(c < '0' || c > '9') {
            if(c == '-')    f = -1;
            c = getchar();
        }
        while(c >= '0' && c <= '9') {
            x = (x << 1) + (x << 3) + (c ^ 48);
            c = getchar();
        }
        return f * x;
    }
    
    const ll INF = 0x3f3f3f3f3f3f3f3f;
    const int N1 = 1e3 + 10, N2 = 1e6 + 10;
    
    int head[N1], to[N2], nex[N2], cnt;
    int visit[N1], dfn[N1], low[N1], scc[N1], n, sum, tot;
    int stk[N1], in[N1], top;
    ll cost[N1];
    
    struct point {
        ll x, y, c, r;
        void input() {
            x = read(), y = read(), r = read(), c = read();
        }
    }a[N1];
    
    ll dis(point a, point b) {
        return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
    }
    
    void add(int x, int y) {
        to[cnt] = y;
        nex[cnt] = head[x];
        head[x] = cnt++;
    }
    
    void tarjan(int rt) {
        dfn[rt] = low[rt] = ++tot;
        stk[++top] = rt;
        visit[rt] = 1;
        for(int i = head[rt]; i; i = nex[i]) {
            if(!dfn[to[i]]) {
                tarjan(to[i]);
                low[rt] = min(low[rt], low[to[i]]);
            }
            else if(visit[to[i]])
                low[rt] = min(low[rt], dfn[to[i]]);
        }
        if(dfn[rt] == low[rt]) {
            sum++;
            do {
                scc[stk[top]] = sum;
                cost[sum] = min(cost[sum], a[stk[top]].c);
                visit[top[stk]] = 0;
                top--;
            }while(stk[top + 1] != rt);
        }
    }
    
    int main() {
        // freopen("in.txt", "r", stdin);
        int t = read();
        for(int cas = 1; cas <= t; cas++) {
            n = read();
            for(int i = 1; i <= n; i++) {
                head[i] = visit[i] = dfn[i] = low[i] = scc[i] = in[i] = 0;
                cost[i] = INF;
                a[i].input();
            }
            tot = sum = top = 0, cnt = 1;
            for(int i = 1; i <= n; i++)
                for(int j = i + 1; j <= n; j++) {
                    ll d = dis(a[i], a[j]);
                    if(d <= a[i].r * a[i].r)
                        add(i, j);
                    if(d <= a[j].r * a[j].r)
                        add(j, i);
                }
            for(int i = 1; i <= n; i++)
                if(!dfn[i])
                    tarjan(i);
            for(int i = 1; i <= n; i++)
                for(int j = head[i]; j; j = nex[j])
                    if(scc[i] != scc[to[j]])
                        in[scc[to[j]]]++;
            ll ans = 0;
            for(int i = 1; i <= sum; i++)
                if(in[i] == 0)
                    ans += cost[i];
            printf("Case #%d: %lld
    ", cas, ans);
        }
        return 0;
    }
    
  • 相关阅读:
    51nod乘积之和
    Dell服务器安装OpenManage(OMSA)
    Nginx反向代理PHP
    搭建haproxy
    108. Convert Sorted Array to Binary Search Tree
    60. Permutation Sequence
    142. Linked List Cycle II
    129. Sum Root to Leaf Numbers
    118. Pascal's Triangle
    26. Remove Duplicates from Sorted Array
  • 原文地址:https://www.cnblogs.com/lifehappy/p/13051569.html
Copyright © 2011-2022 走看看