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;
    }
    
  • 相关阅读:
    面试题|Docker的优缺点
    【华为出品】物联网全栈开发实战营来啦!送海思双目开发板
    Nginx实战|Nginx健康检查
    Linux中几个正则表达式的用法
    盘点提高国内访问 Github 的速度的 9 种方案
    一行代码如何隐藏 Linux 进程?
    (二)类加载机制与反射:类加载器
    (一)类加载机制与反射:类的加载,连接和初始化
    (八)多线程:线程相关类
    (七)多线程:线程池
  • 原文地址:https://www.cnblogs.com/lifehappy/p/13051569.html
Copyright © 2011-2022 走看看