• poj3177


    题意:求最少添加多少条边可变无桥的连通图。

    分析:接着poj3352的看。对于这种题,我们正常的做法是求桥,删桥,求连通分支,缩点,构建新图,求叶子数。

    我们有一种简便方法。需要对tarjan算法做一些变化。我们之前规定low[u]是其子孙通过一条返祖边直接到达的点,把这个改成是其子孙可以连续通过多条返祖边所能到达的点。那么low[u]=min(low[v],dfn[u]);

    这样做的缺陷是,不能求割点了,多次返祖会导致求割点的错误,在多环两两以单个点相连排成一条线,且每两个连接点间只有一条边的情况中,那些连接点本应是割点,但是在dfs过程中,这些连接点之间的边又恰好不是树枝边的话,low[u]可能会通过多次返祖,从一个割点不断的经过这些割点到达最上边的割点才记录下low[u]。

    这样中间的割点就都不符合dfn(u)<=low[v]了。

    但是这样做有一个好处,就是所有的对于边的双连通分支都以low标记出来了,即属于同一双连通分支的所有点的low都等于同一个值。因为在不遇到桥的情况下,low可以返祖到该连同分支在遍历树中的最高点(dfn最小的点)。

    这样就相当于整理出了所有的对于边的双连通分支。接下来计算新图中每个点的度,我们直接遍历所有的边,观察边的两端点是否属于同一分支,若不属于则把两点在新图中的度数+1。然后看有多少个度数为1的点(即叶子数),再通过公式计算即可。

    这题有重边,注意判断,新图添加度数时不计算重边的度。

    View Code
    #include <iostream>
    #include
    <cstdio>
    #include
    <cstdlib>
    #include
    <cstring>
    usingnamespace std;

    #define maxn 5005
    #define maxm 10005

    struct Edge
    {
    int v, next;
    } edge[maxm];

    int n, m;
    int head[maxn];
    bool hash[maxn][maxn];
    int ecount, tcount;
    int dfn[maxn], vis[maxn], low[maxn], degree[maxn];

    void addedge(int a, int b)
    {
    edge[ecount].v
    = b;
    edge[ecount].next
    = head[a];
    head[a]
    = ecount;
    hash[a][b]
    = hash[b][a] =true;
    ecount
    ++;
    }

    void input()
    {
    memset(head,
    -1, sizeof(head));
    ecount
    =0;
    scanf(
    "%d%d", &n, &m);
    for (int i =0; i < m; i++)
    {
    int a, b;
    scanf(
    "%d%d", &a, &b);
    a
    --;
    b
    --;
    if (hash[a][b])
    continue;
    addedge(a, b);
    addedge(b, a);
    }
    }

    void dfs(int fa, int u)
    {
    vis[u]
    =true;
    low[u]
    = dfn[u] = tcount++;
    for (int i = head[u]; i !=-1; i = edge[i].next)
    {
    int v = edge[i].v;
    if (v == fa)
    continue;
    if (!vis[v])
    dfs(u, v);
    low[u]
    = min(low[u], low[v]);
    }
    }

    int tarjan()
    {
    memset(dfn,
    0, sizeof(dfn));
    memset(vis,
    0, sizeof(vis));
    memset(degree,
    0, sizeof(degree));
    tcount
    =0;
    dfs(
    0, 0);
    int ret =0;
    for (int i =0; i < n; i++)
    for (int j = head[i]; j !=-1; j = edge[j].next)
    {
    int v = edge[j].v;
    if (low[i] != low[v])
    degree[low[i]]
    ++;
    }
    for (int i =0; i < n; i++)
    if (degree[i] ==1)
    ret
    ++;
    return (ret +1) /2;
    }

    int main()
    {
    //freopen("t.txt", "r", stdin);
    input();
    int ans = tarjan();
    printf(
    "%d\n", ans);
    return0;
    }
  • 相关阅读:
    类似详情表里面查询最后一次下的订单(以下示例是查找最近一次登陆的记录)
    你真的理解了继承和多态吗?
    尝试了N个版本的Visual C++ 2005后,终于这个Visual C++ 2005 Express Beta 2可以用了。
    [Eclipse笔记]请对Java、Sun、NetBeans、Eclipse感兴趣朋友的看看Eclipse对Sun的心态吧
    [Eclipse笔记]无意中发现Eclipse3.1M7中增加的一项虽然小却很方便的功能
    [Eclipse笔记]Back to the old days Eclipse下的二进制文件编辑器插件EHEP
    [Eclipse笔记]Eclipse3.1M7在Windows下新的内存管理方式
    Is JBuilder dead?
    [Eclipse笔记]SQLExplorer插件试用手记
    [Eclipse笔记]Bug 21493 fixed
  • 原文地址:https://www.cnblogs.com/rainydays/p/2099524.html
Copyright © 2020-2023  润新知