HDOJ 4612 - Warm up tarjan求无向图双联通分量+树形DP+stack申请更多空间

        题意:

                  给一个连通无向图..两点间可能有多条边...问加一条边后..桥最少能有多少个...

        题解:

                 之前没写过tarjan求双联通分量的...练习赛的时候思路有点乱.再加上不断的爆栈..就搞不定了..

                 首先求双联通分量并缩点..如果题目能保证没有重边..做了无向图tarjan后每个点的low值就代表在哪个双连通分量里....但题目就是要绕一下..那么就先求出哪些边是桥..然后dfs染色..碰到桥不过去..就行..再将颜色相同的点缩成一个..做边....这时这个图就成了一颗树..题目是说加一条边后..要让桥最少..显然是要连接某两个度为1的点( 叶子节点或者说只有一个孩子的根节点)...链接这两点后..又出来一个双联通块..双联通块中的桥全部变成非桥..题目再次转化..求用双联通缩点后的树中的最长链..很典型的树形DP....

                  题目恶心的是会爆栈...真心不想手动写栈做dfs了...提供一种方法..再程序上方加入#pragma comment(linker,"/STACK:102400000,102400000") 用C++提交就行


Program:

#pragma comment(linker,"/STACK:102400000,102400000") //申请栈空间..用C++提交
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<cmath>
#include<queue>
#include<stack>
#include<set>
#include<map>
#include<algorithm>
#define ll long long
#define eps 1e-5
#define oo 1000000007
#define pi acos(-1.0)
#define MAXN 200005
#define MAXM 2000005 
using namespace std;  
struct node
{
       int x,y,id,next; 
}line[MAXM];
int Lnum,_next[MAXN],dfn[MAXN],low[MAXN],DfsIndex,dp[MAXN],tp[MAXN],tpnum,MM;
bool used[MAXN],brige[MAXM];
void addline(int x,int y,int id)
{
       line[++Lnum].next=_next[x],_next[x]=Lnum;
       line[Lnum].x=x,line[Lnum].y=y;
       line[Lnum].id=id;
}
void tarjan(int x,int id)
{
       int y,k;
       dfn[x]=low[x]=++DfsIndex;
       for (k=_next[x];k;k=line[k].next)
       { 
               if (line[k].id==id) continue;
               y=line[k].y;
               if (!dfn[y])
               {
                   tarjan(y,line[k].id);
                   low[x]=min(low[x],low[y]);
                   if (dfn[x]<low[y]) brige[k]=true;
               }else
                   low[x]=min(low[x],dfn[y]);
       }       
       return;
}
void dfs0(int x)
{
       int k;
       tp[x]=tpnum;
       for (k=_next[x];k;k=line[k].next) 
           if (!tp[line[k].y] && !brige[k])
               dfs0(line[k].y);
}
void dfs(int x)
{
       int y,k;
       dp[x]=0;
       used[x]=true;
       for (k=_next[x];k;k=line[k].next)
       {
                y=line[k].y;
                if (used[y]) continue;
                dfs(y);
                MM=max(MM,dp[x]+dp[y]+1);
                dp[x]=max(dp[x],dp[y]+1);
       }
       return;
}
int main()
{    
       int N,M,i; 
       freopen("input.txt","r",stdin);
       freopen("output.txt","w",stdout);
       while (~scanf("%d%d",&N,&M) && ( N || M) )
       {
                Lnum=0; 
                memset(_next,0,sizeof(_next));
                while (M--)
                {
                       int x,y;
                       scanf("%d%d",&x,&y);
                       addline(x,y,M),addline(y,x,M);
                }     
                memset(dfn,0,sizeof(dfn));
                memset(brige,false,sizeof(brige));
                DfsIndex=0;
                tarjan(1,-1);
                tpnum=0;
                memset(tp,0,sizeof(tp));
                for (i=1;i<=N;i++)
                   if (!tp[i])
                   {
                          tpnum++;
                          dfs0(i);
                   }
                memset(_next,0,sizeof(_next));
                int x,y,temp=Lnum;
                Lnum=0;
                for (i=1;i<=temp;i++)
                {
                        x=tp[line[i].x],y=tp[line[i].y];
                        if (x==y) continue;
                        addline(x,y,0);
                }
                memset(used,false,sizeof(used));
                MM=0;
                dfs(1);              
                printf("%d\n",tpnum-1-MM);
       }
       return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值