[ZJOI2004]嗅探器

题目概要:

在无向图中寻找出所有的满足下面条件的点:割掉这个点之后,能够使得一开始给定的两个点a和b不连通,割掉的点不能是a或者b。(ZJOI2004)

数据范围约定
结点个数N≤100
边数M≤N*(N-1)/2

朴素算法:

枚举每个点,删除它,然后判断a和b是否连通,时间复杂度O(NM)
如果数据范围扩大,该算法就失败了!

AC算法:

题目要求的点一定是图中的割点,但是图中的割点不一定题目要求的点。如上图中的蓝色点,它虽然是图中的割点,但是割掉它之后却不能使a和b不连通
由于a点肯定不是我们所求的点,所以可以以a为根开始DFS遍历整张图。
对于生成的DFS树,如果点v是割点,如果以他为根的子树中存在点b,那么该点是问题所求的点。

时间复杂度是O(M)的

code:

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=105;
int read() {
    int x=0,f=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
int n,a[N][N],deep[N],dfn[N],size[N],low[N],fa[N],timer,u,v,ans;
void tarjan(int k) {
    dfn[k]=low[k]=++timer;
    size[k]=1;
    for (int i=1;i<=n;i++)
        if (a[k][i]) {
            if (!dfn[i]) {
                tarjan(i);
                fa[i]=k;
                size[k]+=size[i];
                low[k]=min(low[k],low[i]);
                if (dfn[i]<=dfn[v]&&dfn[i]+size[i]-1>=dfn[v]&&low[i]>=dfn[k]&&k!=u&&k!=v) ans=min(ans,k);
            }
        else low[k]=min(low[k],dfn[i]);
        }
}
int main() {
    n=read();
    u=read(),v=read();
    while (u!=0) {
        a[u][v]=a[v][u]=1;
        u=read(),v=read();
    }
    u=read(),v=read();
    ans=n+1;
    tarjan(u);
    if (ans>n) puts("No solution");
    else printf("%d
",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/zzrblogs/p/10464513.html