无向图的割顶(poj1523,1144)

割顶:表示无向图中的点,这个点删除之后,原图不在联通,这样的点就是割顶。

怎么求一个图中的割顶呢?

把无向图变成一颗树,dfs时候搜索到在dfs树上的称为树边,搜索是出现后代指向祖先的边称为反向边。

对于根节点,当他存在两个或两个以上的子节点时,那么他就是割顶。

而对于其他节点u,当且仅当u存在一个子节点v,使得v及其所有的后代都没有反向边连回u的祖先时,u是一个割顶。

那么判断就很简单,这里给出两个模板:

题目:poj1523 和 1144都是裸的求割顶的题目

通用模板:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;

const int N=1100;

int dfn[N],low[N],vis[N],cnt[N];
///dfn 表示深搜的时候的标号   low表示他能连到最早的点
bool cut[N];
int k,n,root;
vector<int> g[N];

void addedge(int x,int y){
    g[x].push_back(y);
    g[y].push_back(x);
}
void Tarjan(int u,int fa)
{
    int son = 0;
    vis[u] = 1;
    dfn[u] = low[u] = ++k;
    for(int i=0;i<g[u].size();i++)
    {
        int v = g[u][i];
        if(vis[v]==1 && v!=fa)
            low[u] = min(low[u],dfn[v]);
        if(vis[v]==0)
        {
            Tarjan(v,u);
            son++;
            low[u] = min(low[u],low[v]);
            if((u==root && son>1) || ( u!=root && low[v]>=dfn[u] ) )
                cut[u]= 1,cnt[u]++;
        }
    }
    vis[u] = 2;
}
int main()
{
//    freopen("Input.txt","r",stdin);
    int cas = 1;
    while(true)
    {
        int x,y,n = 0;
        scanf("%d",&x);
        if(x==0)
            break;
        scanf("%d",&y);
        addedge(x,y);
        n = max(n,x),n = max(n,y);
        while(~scanf("%d",&x) && x)
        {
            scanf("%d",&y);
            n = max(n,x);
            n = max(n,y);
            addedge(x,y);
        }
        k = 0;
        memset(dfn,0,sizeof(dfn));
        memset(low,0,sizeof(low));
        memset(vis,0,sizeof(vis));
        memset(cut,0,sizeof(cut));
        memset(cnt,0,sizeof(cnt));
        root = 1;
        cnt[root] = 0;  ///注意初始点会多算一次,因为它本身会把它的子节点都计算出来
        Tarjan(root,-1);
        int ans = 0;
        for(int i=1;i<=n;i++)
            if(cut[i])
                ans++;
        printf("Network #%d\n",cas++);
        if(ans==0)
            puts("  No SPF nodes");
        else
        {
            for(int i=1;i<=n;i++)
            {
                if(cut[i])
                    printf("  SPF node %d leaves %d subnets\n",i,cnt[i]+1);
            }
        }
        puts("");
        for(int i=0;i<=n;i++)
            g[i].clear();
    }
    return 0;
}

还有一个大白书上的模板,写法差不多,但是提交到1144上wa了

#include <iostream>
#include <algorithm>
#include <string>
#include <math.h>
#include <vector>
#include <cstring>
#include <cstdio>
#include <stack>
#include <map>
using namespace std;
#define Del(a,b) memset(a,b,sizeof(a))
const int N = 1100;
const int inf = 0x3f3f3f3f;
vector<int> v[N];
int dfs_clock;
int pre[N],cnt[N];
bool iscut[N];
int dfs(int now,int fa)
{
    int lownow = pre[now] = ++dfs_clock;
    int child = 0;
    for(int i=0;i<v[now].size();i++)
    {
        int to = v[now][i];
        if(pre[to]==0)
        {
            child++;
            int lowto = dfs(to,now);
            lownow = min(lowto,lownow);
            if(lowto >= pre[now])  ///here isn‘t equer?
            {
                iscut[now] = true;
                cnt[now]++;
            }
        }
        else if(pre[to] < pre[now] && to!=fa)  ///erevy update
            lownow = min(lownow,pre[to]);
    }
    if(fa<0 && child==1)
        iscut[now] = false;
    ///low[u] = lownow;  if want
    return lownow;
}

int main()
{
//    freopen("Input.txt","r",stdin);
    int cas = 1;
    while(true)
    {
        int x,y,n = 0;
        scanf("%d",&x);
        if(x==0)
            break;
        scanf("%d",&y);
        v[x].push_back(y);v[y].push_back(x);
        n = max(n,x),n = max(n,y);
        while(~scanf("%d",&x) && x)
        {
            scanf("%d",&y);
            n = max(n,x);
            n = max(n,y);
            v[x].push_back(y);
            v[y].push_back(x);
        }
        dfs_clock = 0;
        for(int i=0;i<=n;i++)
        {
            pre[i] = 0;
            cnt[i] = 1;
            iscut[i] = false;
        }
        cnt[1] = 0;  ///注意初始点会多算一次,因为它本身会把它的子节点都计算出来
        dfs(1,-1);
        int ans = 0;
        for(int i=1;i<=n;i++)
            if(iscut[i])
                ans++;
        printf("Network #%d\n",cas++);
        if(ans==0)
            puts("  No SPF nodes");
        else
        {
            for(int i=1;i<=n;i++)
            {
                if(iscut[i])
                    printf("  SPF node %d leaves %d subnets\n",i,cnt[i]);
            }
        }
        puts("");
        for(int i=0;i<=n;i++)
            v[i].clear();
    }
    return 0;
}
时间: 2024-12-27 16:31:09

无向图的割顶(poj1523,1144)的相关文章

图论(无向图的割顶):POJ 1144 Network

Network Description A Telephone Line Company (TLC) is establishing a new telephone cable network. They are connecting several places numbered by integers from 1 to N . No two places have the same number. The lines are bidirectional and always connect

无向图求割顶与桥

无向图求割顶与桥 对于无向图G,如果删除某个点u后,连通分量数目增加,称u为图的关节点或割顶.对于连通图,割顶就是删除之后使图不再连通的点.如果删除边(u,v)一条边,就可以让连通图变成不连通的,那么边(u,v)是桥. 具体的概念和定义比较多,在刘汝佳<<训练指南>>P312-314页都有详细的介绍. 下面来写求无向图割顶和桥的DFS函数.我们令pre[i]表示第一次访问i点的时间戳,令low[i]表示i节点及其后代所能连回(通过反向边)的最早祖先的pre值. 下面的dfs函数返回

【模板】无向图的割顶

无向图的割顶: Vector <int> G[] :邻接表存图 Int pre[] :存储时间戳 Int low[] : u及其后代所能连回的最早的祖先的pre值 Int iscut[] : =true表示是割顶,=false不是割顶 Dfs函数在主函数调用时,fa预设为-1. vector <int> G[MAXN]; int pre[MAXN],iscut[MAXN],low[MAXN],dfs_clock; int dfs(int u,int fa) { int lowu=p

连通分量 无向图的割顶和桥 无向图的双连通分量 有向图的强连通分量

时间戳 dfs_clock :说白了就是记录下访问每个结点的次序.假设我们用 pre 保存,那么如果 pre[u] > pre[v], 那么就可以知道先访问的 v ,后访问的 u . 现在给定一条边, (u, v), 且 u 的祖先为 fa, 如果有 pre[v] < pre[u] && v != fa, 那么 (u, v) 为一条反向边. 1 求连通分量: 相互可达的节点称为一个连通分量: #include <iostream> #include <cstd

UVA 315 :Network (无向图求割顶)

题目链接 题意:求所给无向图中一共有多少个割顶 用的lrj训练指南P314的模板 #include<bits/stdc++.h> using namespace std; typedef long long LL; const int N=109; struct Edge { int to,next; Edge(){} Edge(int _to,int _next) { to=_to; next=_next; } }edge[N*N*2]; int head[N]; int dfn[N],lo

无向图的割顶和桥

割顶: 关键点,删掉这个点后,图的连通分量 + 1: 桥: 在割顶的基础上,发现删除 (u,v) 这条边,图就变成非连通的了. 如何找出所有割顶和桥: 时间戳: 在无向图的基础上,DFS建树的过程中,各点进栈和出栈的时间 dfs_clock,进栈的时间 pre[],出栈的时间 post[] 在DFS程序中的体现就是: void previst(int u) { pre[u]= ++dfs_clock; } void postvist(int u) { post[u] = ++dfs_clock;

无向图的割顶和桥的性质 以及双连通分量的求解算法

割顶:对于无向图G,如果删除某个点u后,连通分量的数目增加, 称u为图的割顶.对于连通图,割顶就是删除之后使图不再连通的点. 割顶的求解依如下定理: 在无向连通图G的DFS树中,非根结点u是G的割顶当且仅当u存在一个子节点v,使得v及其所有后代都没有反向边连回u的祖先(连回u)不算. 算法实现: 采用时间戳,在dfs遍历的过程中给每个节点u均标记以前序时间戳pre[u],设low[u]为u及其后代所能连回的最早的祖先的pre值,则定理中的条件就可以简写成结点u存在一个子结点v,使得 low[v]

无向图的割顶和桥,无向图的双连通分量入门详解及模板 -----「转载」

https://blog.csdn.net/stillxjy/article/details/70176689 割顶和桥:对于无向图G,如果删除某个节点u后,连通分量数目增加,则称u为图的割顶:如果删除某条边后,连通分量数目增加,则称该边为图的桥.对于连通图删除割顶或桥后都会使得图不再连通 以下我,我们利用dfs的性质来快速找出一个连通图中的所有的割顶和桥 首先我们要引入”时间戳”这个概念: 时间戳:表示在进行dfs时,每个节点被访问的先后顺序.每个节点会被标记两次,分别用pre[],和post

poj 1144 Network【无向图求割顶模板题】

Description A Telephone Line Company (TLC) is establishing a new telephone cable network. They are connecting several places numbered by integers from 1 to N. No two places have the same number. The lines are bidirectional and always connect together