HDU 4612 Warm up (缩点+树的直径,有重边)

本文介绍了一个关于图论的问题,即如何通过添加一条边来最小化图中桥的数量。文章详细讨论了处理该问题的具体算法实现,包括考虑重边的影响、使用DFS寻找树的直径以及手动扩大栈空间等技巧。

题意:加一条边最少还剩多少桥。

这题一想了一会就想到了缩点后求树的直径,第一遍敲,树的直径我用的是一个两遍dfs,并且没考虑重边,但报的TLE,第二遍换bfs写不过仍然没考虑重边 wa了。

考虑重边,用map或者set标记,重边肯定不是桥,结果MLE了。

期间再百度了一下树的直径,一般有三种方法,我觉得最简单的一种应该是只要我之前的第一遍dfs,然后利用性质:直径一定是某点在其子树下最大和次大之和。换回dfs写法,当然还是MLE。

看题解,学习下重边怎么办。

kuangbin是直接把所以的边排序,直接把重边找出来,(其实我的思路也是这样,不过找的方法不好,STL导致MLE了)但这样做代码写起来很多很乱。

更好的方法是:在dfs的时候,让v可以通过重边回到父亲节点,而不能通过原来那条边回到父亲节点。

在没有重边的tarjan写法里是遇到父亲就直接continue。现在就是要想办法只continue掉没有重边的父亲。

只要给每条边弄个标记:访问过的边不再访问即可(不是访问过的点不再访问)。这样应该是能实现的,也看见有人是这样的写的,但是我自己写就是不知道哪错了,于是最后学了Alex大神的写法,给一条无向边编号(一条无向边等于两条有向边),在tarjan里第二个参数就设为这个编号,如果(u,v)之间的编号和上一条相同(说明是同一条无向边,相当于要回到父亲)则continue,如果存在重边,编号是不一样的,则不会continue。

但是,做了一天脑袋有点晕,我特么的把Stack数组声明成了bool类型的。见鬼似得查错查了几小时,弱小的心灵已经有阴影了。

另外这题还要手动扩栈:加这个

#pragma comment(linker, "/STACK:1024000000,1024000000")


所以:

1、考虑重边

2、手动扩栈

虽然做的很恶心,但是仍然学到了。

【代码】

#include <map>
#include <set>
#include <list>
#include <stack>
#include <vector>
#include <queue>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;

const int N = 200010;
const int M = 3000010;

struct Edge
{
    int next;
    int to;
    int id;
}edge[M], edge2[M];

int head[N];
int head2[N];
int low[N],belong[N],DFN[N];
int instack[N],Stack[N];
int br_cnt, n, m;
int tot, tot2, maxs, TimeN, top, sccnum;

void addedge(int u,int v,int id)
{
    edge[tot] =  {head[u],v,id};
    head[u] = tot++;
}

void addedge2(int u, int v)
{
    edge2[tot2] = {head2[u],v,0};
    head2[u] = tot2++;
}

void init()
{
    TimeN = sccnum = top = tot = tot2 = maxs = br_cnt = 0;
    memset ( head, -1, sizeof(head) );
    memset (head2, -1, sizeof(head2) );
    memset (DFN, 0, sizeof(DFN) );
    memset ( instack, 0, sizeof(instack) );
}
void tarjan(int u, int fa)
{
    Stack[top++] = u;
    instack[u] = 1;
    DFN[u] = low[u] = ++TimeN;
    for (int i = head[u]; i != -1; i = edge[i].next)
    {
        int v = edge[i].to;
        if (fa == edge[i].id)   continue;
        if ( !DFN[v] )
        {
            tarjan(v, edge[i].id);
            low[u] = min(low[u], low[v]);
            if (DFN[u] < low[v])
                br_cnt++;
        }
        else if(instack[v])
            low[u] = min(DFN[v], low[u]);
    }
    if (DFN[u] == low[u])
    {
        sccnum++;
        int v;
        do
        {
            v = Stack[--top];
            instack[v] = 0;
            belong[v] = sccnum;
        }while(u!=v);
    }
}


int mx[N],smx[N];
void dfs(int u,int fa)  //直接等于某点最大加次大
{
    mx[u] = smx[u] = 0 ;
    for(int i=head2[u]; ~i; i=edge2[i].next)
    {
        int v=edge2[i].to;
        if(v==fa) continue;
        dfs(v,u);
        int tmp = mx[v] + 1;
        if(tmp >= mx[u])
        {
            smx[u] = mx[u];
            mx[u] = tmp;
        }
        else if(tmp > smx[u])
            smx[u] = tmp;
    }
}
void solve()
{
    tarjan(1, -1);
    for (int u = 1; u <= n; ++u)  //缩点建新图
    {
        for (int i = head[u]; i != -1; i = edge[i].next)
        {
            int v = edge[i].to;
            if (belong[u] != belong[v])
                addedge2(belong[u],belong[v]);
        }
    }
    dfs(1,-1);
    for(int i=1;i<=n;i++) maxs = max(maxs , mx[i]+smx[i]);
    printf("%d\n", br_cnt - maxs);
}

int main()
{
    while (~scanf("%d%d", &n, &m) && (n||m))
    {
        init();
        int u, v;
        for (int i = 1; i <= m; ++i)
        {
            scanf("%d%d", &u, &v);
            addedge(u, v, i);
            addedge(v, u, i);
        }
        solve();
    }
    return 0;
}



评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值