• Financial Crisis HDU


    原题链接

    • 题解:算是比较心累的一道题了,发现复杂的图论题尤其是涉及联通分量,就是大模拟+分类讨论。题中意思是,问两个点,是否能有两条不同路径。不同是指没有经过同一个点除了起点和终点。所以如果在同一个连通分量中,并且连通分量的大小大于2,那么必然是两条及以上。如果两个点不在一个集合,即没有道路。如果两个点,这两个点所在的所有连通分量,没有满足,相同并且大小大于2,那么这两个点之间只有一条路径。
    1. 首先第一个对我来说不会的是,如何使每个连通分量包含他们里面的点?即表示有个割点 (a)(A)(B) 中,从 (A) 中得到 (a) 信息,从 (B) 中得到 (a) 信息。那么我们这个栈就不是储存点的了,是储存的是属于这个连通分量的边,然后把边上所有点都加进来就行了,注意,那么如果边 (a-b)(b-c) 都加,那么 (b) 就加了两边,连通分量大小会不对,那么就用 (mark) 判一下重复。
    if (mark[u] != dcc) 
        id[u].push_back(dcc),sz[dcc]++, mark[u] = dcc;
    if (mark[v] != dcc)
        id[v].push_back(dcc), sz[dcc]++, mark[v] = dcc;
    
    1. 分类讨论时,就直接遍历每个点所在的连通分量,二重 (for) 循环暴力跑,然后就可以判是否存在这两个点都在一个 (size > 2) 的连通分量中了。
    • 代码:
    #include <iostream>
    #include <vector>
    #include <cstring>
    #include <cstdio>
    #include <queue>
    #include <map>
    
    using namespace std;
    const int N = 101000;
    const int M = 401000;
    struct edge {
        int u, v;
    }e[N];
    int h[M], ne[M], to[M], idx;
    int dfn[N], low[N], times;
    int f[N];
    int sz[N];
    int mark[N];
    int stk[N], top;
    int dcc = 0;
    int cut[N];
    void add(int u, int v) {ne[idx] = h[u], to[idx] = v, h[u] = idx++;}
    vector<int>id[N];
    void tarjan(int u, int fa) {
        dfn[u] = low[u] = ++times;
        
        int cnt = 0;
        for (int i = h[u]; i != -1; i = ne[i])
        {
            
            int v = to[i];
            if (!dfn[v]) {
                            e[++top] = {u, v};
                tarjan(v, u);
                cnt++;
                low[u] = min(low[u], low[v]);
                if (dfn[u] <= low[v]) {
                    if (u != fa)cut[u] = 1;
                    else cut[u] |= (cnt > 1);
                    dcc++;
                    if (mark[u] != dcc) 
                    id[u].push_back(dcc),sz[dcc]++, mark[u] = dcc;
                    if (mark[v] != dcc)
                        id[v].push_back(dcc), sz[dcc]++, mark[v] = dcc;
                    while (e[top].u != u || e[top].v != v) {
                        int uu = e[top].u;
                        int vv = e[top].v;
                        if (mark[uu] != dcc)
                            id[uu].push_back(dcc), sz[dcc]++, mark[uu] = dcc;
                        if (mark[vv] != dcc)
                            id[vv].push_back(dcc), sz[dcc]++, mark[vv] = dcc;
                        top--;
                    }top--;
                }
            } else
                low[u] = min(low[u], dfn[v]);
        }
    }
    int Find(int x){return f[x] == x?x:f[x] = Find(f[x]);}
    void Un(int x, int y) {
        int fx = Find(x);
        int fy = Find(y);
        if (fx == fy)return;
        f[fx] = fy;
    }
    int cas = 0;
    void solve() {
        int n, m, q;
        int cas = 0;
        while (~scanf("%d%d%d", &n, &m, &q)) {
            if (n == 0 && q == 0 && m == 0)break;
            memset(h, -1, sizeof h);
            top = 0;
            dcc = 0;
            memset(id, 0, sizeof id);
            memset(dfn, 0, sizeof dfn);
            memset(sz, 0, sizeof sz);
            memset(mark, 0, sizeof mark);
            idx = times = 0;
            for (int i = 0; i < n; i ++) {
                f[i] = i;
                id[i].clear();
            }
            for (int i = 1; i <= m; i ++) 
            {
                int u, v;scanf("%d%d", &u, &v);
                add(u, v);
                add(v, u);
                Un(u, v);
            }
            for (int i = 0; i < n; i ++) {
                if (!dfn[i])tarjan(i, i);
            }
            printf("Case %d:
    ", ++cas);
            while (q--) {
                int u, v;scanf("%d%d", &u, &v);
                int fx = Find(u);
                int fy = Find(v);
                if (fx != fy) {
                    puts("zero");
                } else {
                    bool f = 0;
                    for (auto uu:id[u]) {
                        for (auto vv:id[v]) {
                            if (vv == uu) {
                                if (sz[vv] > 2) {
                                    f = 1;
                                    puts("two or more");
                                }
                            }
                            if(f)break;
                        }
                        if (f)break;
                    }
                    if (!f) puts("one");
                }
            }
        }
    }
    int main() {
        //ios::sync_with_stdio(0);
        int t = 1;//cin >> t;
        while (t--)solve();
        return 0;
    }
    

    还有一份精简版的,求割点和点双连通分量。

    #include <iostream>
    #include <vector>
    #include <cstring>
    #include <cstdio>
    #include <queue>
    #include <map>
    
    using namespace std;
    const int N = 101000;
    const int M = 401000;
    int h[M], ne[M], to[M], idx;
    int dfn[N], low[N], times;
    int f[N];
    int sz[N];
    int stk[N], top;
    int dcc = 0;
    int cut[N];
    void add(int u, int v) {ne[idx] = h[u], to[idx] = v, h[u] = idx++;}
    vector<int>bl[N];
    void tarjan(int u, int fa) {
        dfn[u] = low[u] = ++times;
        stk[++top] = u;
        int cnt = 0;
        for (int i = h[u]; i != -1; i = ne[i])
        {
            int v = to[i];
            if (!dfn[v]) {
                tarjan(v, fa), ++cnt;
                low[u] = min(low[u], low[v]);
                if (low[v] >= dfn[u]) {
                    if (u != fa)cut[u] = 1;
                    else cut[u] |= (cnt > 1);
                    ++dcc;
                    while (1) {
                        bl[stk[top]].push_back(dcc);//可以bl[dcc].push_back(stk[top]);
                        //主要是看自己需要啥,就咋写
                        sz[dcc]++;
                        if (stk[top--] == v)break;//这里确实有点不好理解,就是说,他这团是把当前这一团外面那一团给算上的,下次补一下图咕咕咕。
                    }
                    bl[u].push_back(dcc);
                    sz[dcc]++;
                }
            } else
                low[u] = min(low[u], dfn[v]);
        }
    }
    int Find(int x){return f[x] == x?x:f[x] = Find(f[x]);}
    void Un(int x, int y) {
        int fx = Find(x);
        int fy = Find(y);
        if (fx == fy)return;
        f[fx] = fy;
    }
    int cas = 0;
    void solve() {
        int n, m, q;
        int cas = 0;
        while (~scanf("%d%d%d", &n, &m, &q)) {
            if (n == 0 && q == 0 && m == 0)break;
            memset(h, -1, sizeof h);
            top = 0;
            dcc = 0;
            memset(id, 0, sizeof id);
            memset(dfn, 0, sizeof dfn);
            memset(sz, 0, sizeof sz);
            memset(mark, 0, sizeof mark);
            idx = times = 0;
            for (int i = 0; i < n; i ++) {
                f[i] = i;
                bl[i].clear();
            }
            for (int i = 1; i <= m; i ++) 
            {
                int u, v;scanf("%d%d", &u, &v);
                add(u, v);
                add(v, u);
                Un(u, v);
            }
            for (int i = 0; i < n; i ++) {
                if (!dfn[i])tarjan(i, i);
            }
            printf("Case %d:
    ", ++cas);
            while (q--) {
                int u, v;scanf("%d%d", &u, &v);
                int fx = Find(u);
                int fy = Find(v);
                if (fx != fy) {
                    puts("zero");
                } else {
                    bool f = 0;
                    for (auto uu:bl[u]) {
                        for (auto vv:bl[v]) {
                            if (vv == uu) {
                                if (sz[vv] > 2) {
                                    f = 1;
                                    puts("two or more");
                                }
                            }
                            if(f)break;
                        }
                        if (f)break;
                    }
                    if (!f) puts("one");
                }
            }
        }
    }
    int main() {
        //ios::sync_with_stdio(0);
        int t = 1;//cin >> t;
        while (t--)solve();
        return 0;
    }
    
  • 相关阅读:
    正定矩阵与半正定矩阵定义与判别
    LQR (线性二次型调节器)的直观推导及简单应用
    simulink模块使用方式
    无人驾驶领域现有的主流智能车辆仿真软件具体情况介绍
    什么是在环测试
    C# Dictionary 是否包含key
    jquery.显示隐藏切换
    easyui-dialog
    Visual Studio 命令行wsdl生成C#操作类
    使用C#创建Windows服务
  • 原文地址:https://www.cnblogs.com/Xiao-yan/p/14632026.html
Copyright © 2020-2023  润新知