题目概要:
在无向图中寻找出所有的满足下面条件的点:割掉这个点之后,能够使得一开始给定的两个点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; }