PKU上几乎一模一样的两道题,都是用tarjan来求双连通分量,唯一不同的就是3177会有重边的情况,而3352不会有,所以在加了判断重边之后的代码在两题提交都能过。
题目:
大神题解:点击打开链接
#include<iostream>
#include<vector>
#include<cstdio>
#include<cstring>
using namespace std;
#define N 5005
vector<int> vec[N];
int step,cl[N],low[N],dfn[N];
bool p[N][N];
void tarjan(int now,int pre)
{
low[now]=dfn[now]=step++;
int len=vec[now].size();
for(int i=0;i<len;i++)
{
int u=vec[now][i];
if(dfn[u]==-1)
{
tarjan(u,now);
low[now]=min(low[now],low[u]);
}
else
{
if(u!=pre)low[now]=min(low[now],dfn[u]);
}
}
}
void setcl(int now)
{
int len=vec[now].size();
for(int i=0;i<len;i++)
{
int u=vec[now][i];
if(cl[u]!=-1)continue;
if(dfn[now]<low[u])cl[u]=step++; //如果不在同一个连通分支里就染不同的色
else cl[u]=cl[now]; //否则就染同一种色
setcl(u);
}
}
int main()
{
//freopen("a.txt","r",stdin);
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)vec[i].clear();
memset(p,false,sizeof(p));
while(m--)
{
int x,y;
scanf("%d%d",&x,&y);
if(p[x][y])continue; //判断重边,这里用到了最愚蠢的方法
p[x][y]=true;
vec[x].push_back(y);
vec[y].push_back(x);
}
step=0;
memset(dfn,-1,sizeof(dfn));
tarjan(1,0);
memset(cl,-1,sizeof(cl));
cl[1]=0;
step=1;
setcl(1);
int du[N];
memset(du,0,sizeof(du));
for(int i=1;i<=n;i++)
{
int len=vec[i].size();
for(int j=0;j<len;j++)
{
int u=vec[i][j];
if(cl[i]!=cl[u])du[cl[i]]++; //如果这点和与它连接的点不同颜色,就增加这点的连通度数
}
}
int leap=0;
for(int i=0;i<step;i++)
{
if(du[i]==1)leap++;
}
printf("%d\n",(leap+1)/2); //因为一条边的两个点被算了一次
return 0;
}