POJ 1523--SPF【无向图的所有割点 && 删去该点后bcc的数目】

SPF

Time Limit: 1000MS   Memory Limit: 10000K
Total Submissions: 7066   Accepted: 3218

Description

Consider the two networks shown below. Assuming that data moves around these networks only between directly connected nodes on a peer-to-peer basis, a failure of a single node, 3, in the network on the left would prevent some of the still available nodes from
communicating with each other. Nodes 1 and 2 could still communicate with each other as could nodes 4 and 5, but communication between any other pairs of nodes would no longer be possible.

Node 3 is therefore a Single Point of Failure (SPF) for this network. Strictly, an SPF will be defined as any node that, if unavailable, would prevent at least one pair of available nodes from being able to communicate on what was previously a fully connected
network. Note that the network on the right has no such node; there is no SPF in the network. At least two machines must fail before there are any pairs of available nodes which cannot communicate.

Input

The input will contain the description of several networks. A network description will consist of pairs of integers, one pair per line, that identify connected nodes. Ordering of the pairs is irrelevant; 1 2 and 2 1 specify the same connection. All node numbers
will range from 1 to 1000. A line containing a single zero ends the list of connected nodes. An empty network description flags the end of the input. Blank lines in the input file should be ignored.

Output

For each network in the input, you will output its number in the file, followed by a list of any SPF nodes that exist.

The first network in the file should be identified as "Network #1", the second as "Network #2", etc. For each SPF node, output a line, formatted as shown in the examples below, that identifies the node and the number of fully connected subnets that remain when
that node fails. If the network has no SPF nodes, simply output the text "No SPF nodes" instead of a list of SPF nodes.

Sample Input

1 2
5 4
3 1
3 2
3 4
3 5
0

1 2
2 3
3 4
4 5
5 1
0

1 2
2 3
3 4
4 6
6 3
2 5
5 1
0

0

Sample Output

Network #1
  SPF node 3 leaves 2 subnets

Network #2
  No SPF nodes

Network #3
  SPF node 2 leaves 2 subnets
  SPF node 3 leaves 2 subnets

Source

Greater New York 2000

第一道双连通分量的题, 求的是无向图的所有割点, 以及删去该点后bcc的数目。

看了图论算法理论的书一上午, 里面概念真心多 ,讲的挺好,但是代码风格不喜欢,后来阿宇让我看刘汝佳的训练指南,上面的概念和图论算法理论有点不用 ,但是更好理解。

先说一下tarjan算法里面几个重要的变量。

(1)dfs[ ] 记录各顶点的深度优先数

(2)low[ ]记录从该点或它的子孙出发 通过回边可以到达的最低深度优先数

更新low[u]有三点:均在tarjan中实现,取则三种中最小的low[ u ]

1,u本身的深度优先数dfn[u]

2,u的子节点中最低深度优先数(对没有查询过的,要每次更新)

3,u通过回边可以到达的最低优先数

节点 u 是 割点的充要条件:

(1) u 是具有两个以上子节点的深度深度优先生成树的根。

(2) u 存在一个 子节点 v 使得 low[ v ] >= dfn[ u ]。

去除割点 u 后,讲原来的连通图分成了几个连通分量

(1)如果割点 u 是根节点, 则有几个子节点,就分成几个连通分量

(2)如果割点 u 不是根节点,则有d个子节点 v, 使得low[ v ] >= dfn[ u ],则去掉 u 节点,分成d + 1个连通分量。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <stack>
#define maxn 1000+10
#define maxm 2000000+10
using namespace std;

struct node {
    int u, v, next;
};

node edge[maxm];
int head[maxn], cnt;
int low[maxn];//从该点或它的子孙出发 通过回边可以到达的最低
int dfn[maxn];//该点的深度优先数
bool is_cut[maxn];//标记该点是不是割点
int add_bcc[maxn];//去掉该点增加的bcc数目
int dfs_clock;//深度优先数计数器
int bccno[maxn];//属于哪个bcc
int bcc_cnt;//bcc计数器

stack<node> S;//存储当前bcc中的边
vector<int>bcc[maxn];//存储bcc里面的点

void init(){
    cnt = 0;
    memset(head, -1, sizeof(head));
}

void addedge(int u, int v){
    edge[cnt] = {u, v, head[u]};
    head[u] = cnt++;
    edge[cnt] = {v, u, head[v]};
    head[v] = cnt++;
}

int tarian(int u, int fa){
    low[u] = dfn[u] = ++dfs_clock;
    int son = 0;
    for(int i = head[u]; i != -1; i = edge[i].next){
        node E = edge[i];
        int v = E.v;
        if(!dfn[v]){
            S.push(E);
            son++;
            low[v] = tarian(v, u);
            low[u] = min(low[u], low[v]);
            if(low[v] >= dfn[u]){ // u 是割点,但不是根节点
                is_cut[u] = true;
                //记录 u 有几个满足low[v] >= dfn[u]的子节点 v,
                //若有d个,去掉该割点后分成了 d + 1个连通分量。(这个 + 1会在后面输出的时候加上)
                add_bcc[u]++;
                //记录bcc里面的点
                bcc_cnt++;
                bcc[bcc_cnt].clear();
                while(1){
                    node A = S.top();
                    S.pop();
                    if(bccno[A.u] != bcc_cnt){
                        bcc[bcc_cnt].push_back(A.u);
                        bccno[A.u] = bcc_cnt;
                    }
                    if(bccno[A.v] != bcc_cnt){
                        bcc[bcc_cnt].push_back(A.v);
                        bccno[A.v] = bcc_cnt;
                    }
                    if(A.u == u && A.v == v) break;
                }
            }
        }
        else if(v != fa && dfn[v] < dfn[u]){
            S.push(E);
            low[u] = min(low[u], dfn[v]);//回边更新
        }
    }
    //u 是根节点, 但它的子节点只有一个,不是割点
    if(fa < 0 && son == 1) is_cut[u] = 0;
    //u 是根节点, 而且它的子节点个数 >= 2,是割点。删去该点后
    //u 有几个子节点, 就分成了几个bcc, 这里 先 son - 1, 后面输出的时候会 + 1.
    if(fa < 0 && son > 1) add_bcc[u] = son - 1;
    return low[u];
}

void find(int l, int r){
    memset(low, 0, sizeof(low));
    memset(dfn, 0, sizeof(dfn));
    memset(add_bcc, 0, sizeof(add_bcc));
    memset(is_cut, 0, sizeof(is_cut));
    memset(bccno, 0, sizeof(bccno));
    dfs_clock = bcc_cnt = 0;
    for(int i = l; i <= r; ++i)
        if(!dfn[i]) tarian(i, -1);
}

int main (){
    int l, r;
    int a, b;
    int k = 1;
    while(1){
        init();
        l = 2000, r = 0;
        scanf("%d", &a);
        if(a == 0) return 0;
        l = min(a, l);
        r = max(a, r);
        scanf("%d", &b);
        l = min(b, l);
        r = max(b, r);
        addedge(a, b);
        while(scanf("%d", &a), a){
            l = min(a, l);
            r = max(a, r);
            scanf("%d", &b);
            l = min(b, l);
            r = max(b, r);
            addedge(a, b);
        }
        find(l, r);
        int flag = 0;
        printf("Network #%d\n", k++);
        for(int i = l; i <= r; ++i){
            if(is_cut[i]){
                flag = 1;
                printf("  SPF node %d leaves %d subnets\n", i, add_bcc[i] + 1);
            }
        }
        if(!flag)
            printf("  No SPF nodes\n");
        printf("\n");
    }
    return 0;
}

输入每个bcc中含有哪些点。

void BCC(){
    printf("have %d BCC(s)\n", bcc_cnt);
    for(int i = 1; i <= bcc_cnt; i++){
        printf("BCC %d have node(s): ", i);
        for(int j = 0; j < bcc[i].size(); j++)
        printf("%d ", bcc[i][j]);
        printf("\n");
    }
}   

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-09 06:53:23

POJ 1523--SPF【无向图的所有割点 && 删去该点后bcc的数目】的相关文章

POJ 1523 SPF(强连通分量求割点)

题目地址:POJ 1523 这题猛的一看..貌似有点难的样子.不过仔细一想,那个每个割点所分成一次子图不就都能找到这个割点一次吗,那么只要记录下它作为割点的次数再+1不就行了.也算是求割点的裸题吧.这个题的输出很坑...需要注意一下.. 代码如下: #include <iostream> #include <cstdio> #include <string> #include <cstring> #include <stdlib.h> #incl

poj 1523 SPF 无向图求割点

SPF Description Consider the two networks shown below. Assuming that data moves around these networks only between directly connected nodes on a peer-to-peer basis, a failure of a single node, 3, in the network on the left would prevent some of the s

POJ 1523 SPF(无向图割顶)

SPF Description Consider the two networks shown below. Assuming that data moves around these networks only between directly connected nodes on a peer-to-peer basis, a failure of a single node, 3, in the network on the left would prevent some of the s

poj 1523 SPF【点双连通求去掉割点后bcc个数】

SPF Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 7246   Accepted: 3302 Description Consider the two networks shown below. Assuming that data moves around these networks only between directly connected nodes on a peer-to-peer basis, a

POJ 2117--Electricity【点双联通 &amp;&amp; 求删去一个点后,图最多有多少块连通】

Electricity Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 4589   Accepted: 1512 Description Blackouts and Dark Nights (also known as ACM++) is a company that provides electricity. The company owns several power plants, each of them sup

POJ 1523 SPF(割点)

大致题意: 给出一个连通的无向图,求哪些点是割点,对于每个割点,求出去掉这个点后连通分量的个数.如果没有割点的话输出" No SPF nodes". 思路: 求割点用tarjan即可,然后要求删除割点后连通分量的个数,每次找到割点,再在从割点dfs即可,也可以直接在tarjan算法中记录child个数.那么如果割点是根,那么答案就是child,不是根的割点答案是child+1 //188K 0MS C++ 2121B #include<cstdio> #include<

POJ 1523 SPF 求割点的好(板子)题!

题意: 给个无向图,问有多少个割点,对于每个割点求删除这个点之后会产生多少新的点双联通分量 题还是很果的 怎么求割点请参考tarjan无向图 关于能产生几个新的双联通分量,对于每个节点u来说,我们判断他是否是割点,即判断是否满足他的儿子v的low[v]>dfn[u] 而这个时候割掉这个点就会让双联通分量增加,所以搞一个数组记录一下这个操作的次数就行 请注意在是否是根节点的问题上特判 !!注意输出格式!! 1 #include<cstdio> 2 #include<algorithm

POJ - 1523 SPF(割点)

题目大意:给出一张图,问去割点后,连通分量的个数有多少 解题思路:割点的水题,套模版就可以 不得不吐槽一下输入.. #include <cstdio> #include <cstring> #define min(a,b) ((a)<(b)?(a):(b)) #define N 1010 #define M 2000010 struct Edge{ int to, next; }E[M]; int head[N], num[N], pre[N], lowlink[N]; in

poj 1523 SPF 求割点以及删除该割点后联通块的数量

SPF Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 7136   Accepted: 3255 Description Consider the two networks shown below. Assuming that data moves around these networks only between directly connected nodes on a peer-to-peer basis, a