双连通分量 之 一本通1520:【 例 1】分离的路径

题面

1520:【例 1】分离的路径
一句话题意:求无向图中,加多少条边成为双连通分量(无桥)。


思路

先用Tarjin求边双连通分量,再缩点,统计叶子节点ans,答案为(ans+1)/2
具体这个答案的分析请看我的另一篇文章。
首先是Tarjin求边双连通分量,求法也请看我的另一篇文章。这里就直接上代码,十分模板,十分基础,十分简单……

stack<int> st;
void tarjin(int k)
{
  dfn[k]=low[k]=++tot,st.push(k);
  for(int i=head[k]; i; i=a[i].nxt)
    if(!vis[a[i].p])
    {
      vis[i]=1;
      if(!dfn[a[i].to]) tarjin(a[i].to),low[k]=min(low[k],low[a[i].to]);
      else low[k]=min(low[k],low[a[i].to]);
    }
    else vis[i]=1;
  if(low[k]==dfn[k])
  {
    co[k]=++num;
    while(st.top()!=k) co[st.top()]=num,st.pop();
    st.pop();
  }
}

然后是统计叶子节点,即统计入度/出度为1的点,也很简单,注意不要重复统计。

for(int i=1; i<=n; i++)
    for(int j=head[i]; j; j=a[j].nxt)
      if(vis[a[j].p])
      {
        vis[j]=0;
        if(co[a[j].to]!=co[i]) in[co[a[j].to]]++,in[co[i]]++;
      }
      else vis[j]=0;//统计入度
  for(int i=1; i<=num; i++) if(in[i]==1) ans++;  //叶子节点

最后就是输出,答案是 (ans+1)/2


代码

直接上代码,不多说。

#include <bits/stdc++.h>
using namespace std;
int co[10005],dfn[10005],low[10005],num,tot;
int cnt,head[5005];
bool vis[5005];
int n,m,x,y,in[5005],ans;
struct node
{
  int to,nxt,p;
} a[20005];
void add(int x,int y)
{
  a[++cnt].to=y,a[cnt].nxt=head[x],a[cnt].p=cnt+1,head[x]=cnt;
  a[++cnt].to=x,a[cnt].nxt=head[y],a[cnt].p=cnt-1,head[y]=cnt;
}//两次连边
stack<int> st;
void tarjin(int k)
{
  dfn[k]=low[k]=++tot,st.push(k);
  for(int i=head[k]; i; i=a[i].nxt)
    if(!vis[a[i].p])
    {
      vis[i]=1;
      if(!dfn[a[i].to]) tarjin(a[i].to),low[k]=min(low[k],low[a[i].to]);
      else low[k]=min(low[k],low[a[i].to]);
    }
    else vis[i]=1;
  if(low[k]==dfn[k])
  {
    co[k]=++num;
    while(st.top()!=k) co[st.top()]=num,st.pop();
    st.pop();
  }
}
int main()
{
  scanf("%d%d",&n,&m);
  for(int i=1; i<=m; i++) scanf("%d%d",&x,&y),add(x,y);
  tarjin(1);//1为根
  for(int i=1; i<=n; i++)
    for(int j=head[i]; j; j=a[j].nxt)
      if(vis[a[j].p])
      {
        vis[j]=0;
        if(co[a[j].to]!=co[i]) in[co[a[j].to]]++,in[co[i]]++;
      }
      else vis[j]=0;//统计入度
  for(int i=1; i<=num; i++) if(in[i]==1) ans++;  //叶子节点
  printf("%d\n",(ans+1)/2);
  return 0;
}

小结

关于思路,十分简单的tarjin缩点,求叶子,再套公式(找规律),这个公式还是比较有用的。
关于细节,注意不要重复计算,所以我这里做了一个a[i].p,作为反向边,防止重数。
还有问题请私信我。


完结撒花!!!
别忘了点赞关注,谢谢!!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。