hdu4612 Warm up 树形dp 桥 强连通分量

本文介绍了一种通过添加新通道来最小化运输系统中桥的数量的方法。利用Tarjan算法求解强连通分量,结合树形DP算法确定最优新增连接位置,确保各星球间连通且桥的数量达到最少。

Warm up

Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65535/65535 K (Java/Others)
Total Submission(s): 2530 Accepted Submission(s): 592


Problem Description
  N planets are connected by M bidirectional channels that allow instant transportation. It's always possible to travel between any two planets through these channels.
  If we can isolate some planets from others by breaking only one channel , the channel is called a bridge of the transportation system.
People don't like to be isolated. So they ask what's the minimal number of bridges they can have if they decide to build a new channel.
  Note that there could be more than one channel between two planets.

Input
  The input contains multiple cases.
  Each case starts with two positive integers N and M , indicating the number of planets and the number of channels.
  (2<=N<=200000, 1<=M<=1000000)
  Next M lines each contains two positive integers A and B, indicating a channel between planet A and B in the system. Planets are numbered by 1..N.
  A line with two integers '0' terminates the input.

Output
  For each case, output the minimal number of bridges after building a new channel in a line.

Sample Input
  
4 4 1 2 1 3 1 4 2 3 0 0

Sample Output
  
0

Source

Recommend
zhuyuanchen520
题目其实就是要求的是加一条边,最终桥最少!这条边加在什么地方呢。如果是在一个连通分量内,加一条边并不能影响最终的结果,所以应该加在缩图后形成树的直径上,所
谓树的直径,也就是树上最长的两条从根到叶子结点的路径,这样就能使得桥的数目减少这个直径的长度,所以最终的桥自然最小了。
那么,我们先用tarjan求强连通分量,就可以得到缩过的图,这样,所有的边都 是桥了,就可以生成一个树了,我们再加一条边使所有的桥要最小,当然,就是加在树的直径上,也就是一个树形dp就可以了,但是这题我们可以在求强连通分量的时候,顺便就把dp求出来了这样,更快,也好理解了!
用dp[i][0] dp[i][1],表示以i为根的最长和第二长的i到叶子结点的路径,dp[i][0] = max(dp[j][0] + i - j 是否为桥?1:0,dp[i][0]);j是i的子结点,如果i - j 是桥,那么就是dp[j][0] + 1的值更新,否则就是dp[j][0](因为没有桥,所以不用加1),dp[i][1],用一样的递推就可以了! 
#pragma comment(linker,"/STACK:102400000,102400000")
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
#define N 200050
#define M 2000050
int dp[N][2],visit[M],edge_m,head[N],edge[M],next[M],dfn[N],low[N],all,ti;
void addedge(int s,int e)
{
    next[edge_m]=head[s],edge[edge_m]=e,head[s]=edge_m++;
    next[edge_m]=head[e],edge[edge_m]=s,head[e]=edge_m++;
}
void tarjan(int u)
{
    //printf("%d ",u);
    int i,j;
    dfn[u]=low[u]=ti++;//初始化
    dp[u][0]=dp[u][1]=0;
    for(i=head[u];i!=-1;i=next[i])
    {
        j=edge[i];
        if(!visit[i>>1])
        {
            visit[i>>1]=1;
            if(dfn[j]==0)
            {
                tarjan(j);
                all+=(dfn[u]<low[j]);//桥的数目
                int temp=dp[j][0]+(dfn[u]<low[j]);//加一个桥
                if(temp>dp[u][0])//当前子树中的第一大,还是第二大
                {
                     dp[u][1]=dp[u][0];
                     dp[u][0]=temp;

                }
                else if(temp>dp[u][1])
                {
                    dp[u][1]=temp;
                }
                low[u]=min(low[u],low[j]);
            }
            else
            {
                low[u]=min(low[u],dfn[j]);
            }
        }

    }
}
int main()
{
    int n,m,i,s,e,ans;
    while(scanf("%d%d",&n,&m)!=EOF&&m+n)
    {
        memset(head,-1,sizeof(head));
        memset(dfn,0,sizeof(dfn));
        memset(low,0,sizeof(low));
        memset(visit,0,sizeof(visit));
        edge_m=0;all=0;
        for(i=0;i<m;i++)
        {
            scanf("%d%d",&s,&e);
            addedge(s,e);
        }
        ti=1;
        tarjan(1);
        ans=0;
        for(i=1;i<=n;i++)
        {
           ans=max(ans,dp[i][0]+dp[i][1]);//一个子树,第一大和第二大的边就可以组成这个子树的最大直经
        }
        printf("%d\n",all-ans);
    }
    return 0;
}


### 使用Tarjan算法计算强连通分量数量 #### 算法原理 Tarjan算法通过深度优先搜索(DFS)遍历有向图中的节点,记录访问顺序和低链值(low-link value),从而识别出所有的强连通分量。当发现一个节点的访问序号等于其最低可达节点编号时,表明找到了一个新的强连通分量。 #### 时间复杂度分析 该方法的时间效率取决于存储结构的选择。对于采用邻接表表示的稀疏图而言,整体性能更优,能够在线性时间内完成操作,即O(n+m)[^4];而针对稠密图则可能退化至平方级别(O(n&sup2;))。 #### Python代码实现 下面给出一段Python程序用于演示如何基于NetworkX库构建并处理带权无环图(DAG),进而求解其中存在的全部SCC及其总数: ```python import networkx as nx def tarjan_scc(graph): index_counter = [0] stack = [] lowlinks = {} index = {} result = [] def strongconnect(node): # Set the depth index for this node to be the next available incrementing counter. index[node] = index_counter[0] lowlinks[node] = index_counter[0] index_counter[0] += 1 stack.append(node) try: successors = graph.successors(node) except AttributeError: successors = graph.neighbors(node) for successor in successors: if successor not in lowlinks: strongconnect(successor) lowlinks[node] = min(lowlinks[node], lowlinks[successor]) elif successor in stack: lowlinks[node] = min(lowlinks[node], index[successor]) if lowlinks[node] == index[node]: scc = set() while True: current_node = stack.pop() scc.add(current_node) if current_node == node: break result.append(scc) for node in graph.nodes(): if node not in lowlinks: strongconnect(node) return result if __name__ == "__main__": G = nx.DiGraph() # Create a directed graph object using NetworkX library edges_list = [(1, 2),(2, 3),(3, 1)] # Define edge list according to sample input data from hdu1269 problem statement[^5] G.add_edges_from(edges_list) components = tarjan_scc(G) print(f"Number of Strongly Connected Components found: {len(components)}") ``` 此段脚本定义了一个名为`tarjan_scc()`的功能函数接收网络对象作为参数,并返回由集合组成的列表形式的结果集,每个子集中包含了构成单个SCC的所有顶点。最后部分展示了创建测试用DAG实例的过程以及调用上述功能获取最终答案的方式。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值