图论数据结构(入门)(更新中)

图论数据结构

01 UVa11902 Dominator

  • 题目链接:UVA11902 Dominator

  • 题目:查找有向树中必须经过某一节点才能到达的节点有哪些

  • 思路:要求必须到达某一节点 a a a ,那么将节点 a a a 删除后原本能到但是现在不能到达的节点便是 a a a 唯一控制的节点。由于数据较小,于是可以依次删除每一节点后, d f s dfs dfs 查看各自不能到达哪些节点即可。

    #include<bits/stdc++.h>
    using namespace std;
    #define rep(i, a, b) for(int i = (a); i <= (b); i++)
    #define per(i, a, b) for(int i = (a); i >= (b); i--)
    #define ll long long
    #define db double
    #define VI vector<int>
    #define PII pair<int, int>
    const db Pi = 3.141592653589793;
    const int INF = 0x7fffffff;
    const int N = 1e2 + 5;
    const db eps = 1e-10;
    int cas, n, m, no[N][N], vis[N]; //no[i][j]表示删除节点 i 时,是否可到达 j 节点
    VI g[N];
    string s;
    void init(){
        memset(no, 0, sizeof(no));
        rep(i, 1, n) g[i].clear();
    }
    void creatfence(){
        s = "+";
        rep(i, 1, 2 * n - 1) s += "-";
        s += "+";
    }
    void dfs(int root, int without){
        vis[root] = 1;
        if(root == without) return;
        no[without][root] = 1;
        for(auto j : g[root]) if(!vis[j]) dfs(j, without);
    }
    int main(){
        cin >> cas;
        rep(k, 1, cas){
            cin >> n;
            init();
            rep(i, 1, n) rep(j, 1, n){
                int x; scanf("%d", &x);
                if(x && i != j) g[i].push_back(j);
            }
            rep(i, 0, n){ //删除 i 节点时,i = 0 时表示什么都不删
                memset(vis, 0, sizeof(vis));
                dfs(1, i);
            }
            printf("Case %d:\n", k);
            creatfence();
            rep(i, 1, n){
                cout << s << endl;
                rep(j, 1, n) printf("|%c", ((no[0][j] && !no[i][j]) ? 'Y' : 'N'));
                printf("|\n");
            }
            cout << s << endl;
        }
    }
    /*
    2
    5
    0 1 1 0 0
    0 0 0 1 0
    0 0 0 1 0
    0 0 0 0 1
    0 0 0 0 0
    1
    1
    */
    

02 UVa11906 Knight in a War Grid

  • 题目链接:UVA11906 Knight in a War Grid

  • 题目:求在所有可到达的点中,对于每个点,可一步到达的操作数量的奇偶分类

  • 思路:这道题 d f s dfs dfs b f s bfs bfs 都可以做

    • 记忆化 d f s dfs dfs:遍历一遍标记出所有可到达的点,再对这些每个点查找可一步到达此点的点的数量
    • b f s bfs bfs:在搜索时直接记录当前点由哪些点到达,累计记录。

    注意当 m = n m=n m=n m = 0 ∣ ∣ n = 0 m=0 || n=0 m=0n=0 时,不是八个方位均可到达,而是四个方位。于是需要去重(这里用 s e t set set 去重)

    //统计每个点可以由几个不同点一步跳到
    #include<bits/stdc++.h>
    using namespace std;
    #define rep(i, a, b) for(int i = (a); i <= (b); i++)
    #define per(i, a, b) for(int i = (a); i >= (b); i--)
    #define ll long long
    #define db double
    #define VI vector<int>
    #define PII pair<int, int>
    const db Pi = 3.141592653589793;
    const int INF = 0x7fffffff;
    const int N = 1e2 + 5;
    const db eps = 1e-10;
    int cas, n, m, r, c, w;
    int g[N][N], vis[N][N], ans[N][N];
    int odd, even;
    queue<PII> q;
    set<PII> st;
    set<PII>::iterator iter1;
    void init(){
        memset(g, 0, sizeof(g));
        memset(vis, 0, sizeof(vis));
        memset(ans, 0, sizeof(ans));
        while(q.size()) q.pop();
        even = odd = 0;
    }
    void bfs(){
        int dx[10] = {n, m, n, -1 * m, -1 * n, m, -1 * n, -1 * m};
        int dy[10] = {m, n, -1 * m, n, m, -1 * n, -1 * m, -1 * n};
        q.push({0, 0});
        vis[0][0] = 1;
        while(q.size()){
            PII top = q.front(); q.pop();
            st.clear();
            rep(i, 0, 7){
                int nx = top.first + dx[i], ny = top.second + dy[i];
                if(nx < 0 || ny < 0 || nx > r - 1 || ny > c - 1 || g[nx][ny] == -1) continue;
                st.insert({nx, ny}); //防止重复计算 
            }
            for(iter1 = st.begin(); iter1 != st.end(); iter1++){
                PII now = *iter1;
                int nx = now.first, ny = now.second;
                ans[nx][ny]++;  //累计每个点的答案
                if(!vis[nx][ny]){
                    vis[nx][ny] = 1;
                    q.push({nx, ny});
                }
            }
        }
    }
    int main(){
        cin >> cas;
        rep(k, 1, cas){
            init();
            cin >> r >> c >> m >> n >> w;
            rep(i, 1, w){
                int x, y; cin >> x >> y;
                g[x][y] = -1;
            }
            bfs();
            rep(i, 0, r - 1) rep(j, 0, c - 1){
                if(vis[i][j]) ans[i][j] % 2 ? odd++ : even++;
            }
            printf("Case %d: %d %d\n", k, even, odd);
        }
    }
    /*
    2
    3 3 2 1
    0
    4 4 1 2
    2
    3 3
    1 1 
    */
    

03 UVA12442 Forwarding Emails

  • 题目链接:UVA12442 Forwarding Emails

  • 思路:注意每个人只能指向唯一一个人,即每个点只有一条有向边指出,于是边数等于点数,则一定有环。

    • 用数组 d e e p deep deep 记录每个人可到达人的数量。要计算节点 x x x d e e p [ x ] deep[x] deep[x],考虑从 x x x 不断深搜下一节点,直到遇到环(过去已到达的节点)。
    • 剪枝:若一个点 y y y 在之前节点的深搜过程中遇到过,那么之前一定有一个节点指向点 y y y,于是点 y y y 一定不是可遍历最多点的答案,那么可跳过。
    //每个点只有一条有向边指出
    #include<bits/stdc++.h>
    using namespace std;
    #define rep(i, a, b) for(int i = (a); i <= (b); i++)
    #define per(i, a, b) for(int i = (a); i >= (b); i--)
    #define ll long long
    #define db double
    #define VI vector<int>
    #define PII pair<int, int>
    const db Pi = 3.141592653589793;
    const int INF = 0x7fffffff;
    const int N = 5e4 + 5;
    const db eps = 1e-10;
    int cas, n, ans, maxn, nxt[N], vis[N], deep[N], st[N];
    void init(){
        memset(nxt, 0, sizeof(nxt));
        memset(st, 0, sizeof(st));
        memset(vis, 0, sizeof(vis));
        ans = maxn = 0;
    }
    int dfs(int now){
        st[now] = 1;  //用作剪枝
        int tmpdeep;
        vis[now] = 1, tmpdeep = 1;
        if(!vis[nxt[now]]) tmpdeep = dfs(nxt[now]) + 1;
        vis[now] = 0;
        return deep[now] = tmpdeep;
    }
    int main(){
        scanf("%d", &cas);
        rep(k, 1, cas){
            scanf("%d", &n);
            init();
            rep(i, 1, n){
                int u, v; scanf("%d%d", &u, &v);
                nxt[u] = v;
            }
            rep(i, 1, n){
                if(st[i]) continue;  //若之前经过此点,则说明一定不是最优解
                dfs(i);
                if(maxn < deep[i]){
                    maxn = deep[i];
                    ans = i;
                }
            }
            printf("Case %d: %d\n", k, ans);
        }
    }
    /*
    3
    3
    1 2
    2 3
    3 1
    4
    1 2
    2 1
    4 3
    3 2
    5
    1 2
    2 1
    5 3
    3 4
    4 5
    */
    

04 UVA572 Oil Deposit

  • 题目链接:UVA572 油田 Oil Deposits

  • 思路:经典洪水问题,依次在每个块中遍历所有连通区域,记录已到达和连通块数量。

    #include<bits/stdc++.h>
    using namespace std;
    #define rep(i, a, b) for(int i = (a); i <= (b); i++)
    #define per(i, a, b) for(int i = (a); i >= (b); i--)
    #define ll long long
    #define db double
    #define VI vector<int>
    #define PII pair<int, int>
    const db Pi = 3.141592653589793;
    const int INF = 0x7fffffff;
    const int N = 1e2 + 5;
    const db eps = 1e-10;
    int n, m, vis[N][N], ans;
    string s[N];
    int dx[10] = {0, 0, 1, -1, 1, 1, -1, -1};
    int dy[10] = {1, -1, 0, 0, 1, -1, 1, -1};
    void dfs(int x, int y){
        vis[x][y] = 1;
        rep(i, 0, 7){
            int nx = x + dx[i], ny = y + dy[i];
            if(nx < 0 || nx > n - 1 || ny < 0 || ny > m - 1) continue;
            if(s[nx][ny] == '*' || vis[nx][ny]) continue;
            dfs(nx, ny);
        }
    }
    int main(){
        while(cin >> n >> m){
            if(!n && !m) break;
            rep(i, 0, n - 1) cin >> s[i];
            ans = 0;
            memset(vis, 0, sizeof(vis));
            rep(i, 0, n - 1) rep(j, 0, m - 1){
                if(s[i][j] == '*') continue;
                if(vis[i][j]) continue;
                dfs(i, j);
                ans++;
            }
            cout << ans << endl;
        }
    
    }
    /*
    1 1
    *
    3 5
    *@*@*
    **@**
    *@*@*
    1 8
    @@****@*
    5 5
    ****@
    *@@*@
    *@**@
    @@@*@
    @@**@
    0 0
    */
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值