题目大意:给出有向图的关系,求最大连通分量中点数,删去某一个点后,求最大点数的最小值是多少。
解题思路:直接枚举删点,枚举每一个点,记录最大连通分量中的点数,求最大值中的最小值即可。是tarjan模板题,详见code。
题目来源:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=4776
code:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <stack>
#include <algorithm>
using namespace std;
const int MAXN = 100+10;
int n,m,u,v,now;
int pre[MAXN],lowlink[MAXN],sccno[MAXN],dfs_clock,scc_cnt,temp;
vector<int> g[MAXN];
stack<int> s;
void Tarjan(int u){
pre[u]=lowlink[u]=++dfs_clock; //时间戳++
s.push(u); //该点入栈
for(int i=0;i<g[u].size();i++){ //枚举每一条边
int v=g[u][i]; //邻接边
if(v==now) continue; //为枚举点,则不访问
if(!pre[v]){ //未被访问
Tarjan(v); //继续向下找
lowlink[u]=min(lowlink[u],lowlink[v]);//更新结点v所能到达的最下次数层
}
else if(!sccno[v]){//如果结点在栈内
lowlink[u]=min(lowlink[u],pre[v]);
}
}
int ttemp;
if(lowlink[u]==pre[u]){ //如果结点u是强连通分量的根
scc_cnt++; //连通分量标号++
ttemp=0; //记录连通分量中点的数量
while(!s.empty()){
int x=s.top(); s.pop(); //取出栈顶元素
ttemp++;
sccno[x]=scc_cnt; //出栈结点x属于scc_cnt标号的强连通分量
if(x==u) break; //回到根节点则break
}
if(ttemp<2) ttemp=0; //只有两个点则置为0
if(temp<ttemp) temp=ttemp; //记录最大的连通分量中的点数
}
}
int slove(){
int ans=200,tans;
for(int i=0;i<n;i++){ //枚举删点
tans=0;now=i; //各种初始化
memset(sccno,0,sizeof(sccno));
memset(pre,0,sizeof(pre));
for(int j=0;j<n;j++){
if(!pre[j] && j!=i){
dfs_clock=scc_cnt=0;temp=0;//初始化连通分量计数器和时间戳
while(!s.empty()) s.pop(); //每次清空栈
Tarjan(j);
if(tans<temp) tans=temp; //最大连通块
}
}
if(ans>tans) ans=tans; //最大中的最小值
}
return ans;
}
int main(){
//freopen("input.txt","r",stdin);
while(~scanf("%d%d",&n,&m)){
for(int i=0;i<n;i++) g[i].clear();//使用前清空
for(int i=0;i<m;i++){ //接受图
scanf("%d%d",&u,&v);
g[u].push_back(v);
}
printf("%d\n",slove());
}
return 0;
}