割点与割边问题

连通性一·割边与割点

题目传送:hihoCoder - 1183 - 连通性一·割边与割点

AC代码:

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <complex>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <sstream>
#include <utility>
#include <iostream>
#include <algorithm>
#include <functional>
#define LL long long
#define INF 0x7fffffff
using namespace std;

const int maxn = 20005;
int n, m;
vector<int> G[maxn];

int dfn[maxn];//用于标记访问次序
int low[maxn];//low[u]为u及其后代所能连回的最早的祖先的dfn值
int vis[maxn];//标记
int pa[maxn];//记录父亲结点
int is_cut[maxn];//记录是否为割点

struct node {
    int u, v;
}ans_b[200005];
int ans_bian;

bool cmp(node a, node b) {
    if(a.u == b.u)return a.v < b.v;
    return a.u < b.u;
}

int cur_index;
void dfs(int u) {
    dfn[u] = low[u] = ++ cur_index;

    vis[u] = 1;
    int child = 0;
    int d = G[u].size();
    for(int i = 0; i < d; i ++) {
        int v = G[u][i];
        if(!vis[v]) {
            child ++;
            pa[v] = u;
            dfs(v);
            low[u] = min(low[u], low[v]);

            if(pa[u] == 0 && child > 1) {
                is_cut[u] = 1;//这里的割点不能存在一个数组里,如果硬要存的话,则需要判重
            }

            if(pa[u] != 0 && low[v] >= dfn[u]) {
                is_cut[u] = 1;
            }

            if(low[v] > dfn[u]) {//而割边的话可以用数组存,因为边只访问一次,不会重复,而点可能访问多次
                ans_b[ans_bian].u = min(u, v);
                ans_b[ans_bian].v = max(u, v);
                ans_bian ++;
            }
        }
        else if(v != pa[u]){
            low[u] = min(dfn[v], low[u]);
        }
    }
}

int main() {
    scanf("%d %d", &n, &m);
    int u, v;
    for(int i = 0; i < m; i ++) {
        scanf("%d %d", &u, &v);
        G[u].push_back(v);
        G[v].push_back(u);
    }

    cur_index = 0;
    ans_bian = 0;
    memset(pa, 0, sizeof(pa));
    memset(vis, 0, sizeof(vis));
    dfs(1);

    //for(int i = 1; i <= n; i ++) cout << low[i] << " "; cout << endl;
    int flag = 0;
    for(int i = 1; i <= n; i ++) if(is_cut[i]){
        if(flag) printf(" ");
        flag = 1;
        printf("%d", i);
    }
    if(!flag) printf("Null");
    printf("\n");
    //cout << "haha" << endl;
    sort(ans_b, ans_b + ans_bian, cmp);
    for(int i = 0; i < ans_bian; i ++) {
        printf("%d %d\n", ans_b[i].u, ans_b[i].v);
    }
    return 0;
}


危险系数

题目传送:蓝桥杯 - 历届试题 危险系数

题目大意:去求有多少个点,使得去掉这个点之后,U,V不再连通。

分析:类似割点,这里因为数据量较小,可以暴力标记所有从U到V的路径,然后给每一个点记录一个权值,权值的含义为这个点在多少条从U到V的路径上,同时用cnt记录有多少条从U到V的路径,最后只要某个点的权值等于cnt的话,说明这个点就是U和V的割点,因为它在从U到V的所有路径上。

注意:还有不连通的情况要特判一下,即cnt=0的时候

AC代码:

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <complex>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <sstream>
#include <utility>
#include <iostream>
#include <algorithm>
#include <functional>
#define LL long long
#define INF 0x7fffffff
using namespace std;

const int maxn = 1005;
int n, m;

vector<int> G[maxn];//整个图
int way[maxn];//记录每一条路的各个结点
int sum[maxn];//记录每个节点在多少条从U到V的路径上
int vis[maxn];//标记数组

int U, V;
int cnt;//用于记录从U到V的路径的个数

void dfs(int u, int n) {//深搜找所有路径
    way[n] = u;
    if(u == V) {//找到一条路径,并return
        cnt ++;
        for(int i = 0; i <= n; i ++) {
            sum[way[i]] ++;
        }
    }
    int d = G[u].size();
    for(int i = 0; i < d; i ++) {
        int v = G[u][i];
        if(!vis[v]) {
            vis[v] = 1;
            dfs(v, n + 1);
            vis[v] = 0;
        }
    }
}

int main() {
    scanf("%d %d", &n, &m);
    int u, v;
    for(int i = 0; i < m; i ++) {
        scanf("%d %d", &u, &v);
        G[u].push_back(v);
        G[v].push_back(u);
    }

    scanf("%d %d", &U, &V);
    memset(vis, 0, sizeof(vis));
    memset(sum, 0, sizeof(sum));
    vis[U] = 1;
    cnt = 0;
    dfs(U, 0);

    if(cnt == 0) {//特判不连通的情况
        printf("-1\n");
        return 0;
    }
    int ans = 0;
    for(int i = 1; i <= n; i ++) {
        //cout << sum[i] << endl;
        if(sum[i] == cnt) {//cnt是总的路径个数,如果相等的话,说明i点在U到V的每一条路径上,即删去改点后U,V不连通
            ans ++;
        }
    }
    printf("%d\n", ans - 2);//减2是因为起始点和结束点不应该包括在里面
    return 0;
}


你可能感兴趣的:(ACM,割点与割边)