Warm up---hdu4612(缩点,树的直径)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4612

给一个无向图, 加上一条边后,求桥最少有几个;

那我们加的那条边的两个顶点u,v;一定是u,v之间含有桥的数量最多,然后uv之间的桥都没了,剩下的就是要求的结果;

树的直径的定义刚好就是两个节点之间含有最多的边;

下面是有关树的直径的知识;

 

这个题目需要手动扩展,不然会爆栈,而且手动扩展的话要用C++提交。

 

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<stdio.h>
#include<string.h>
#include<queue>
#include<algorithm>
using namespace std;
#define N 200005
int Head1[N], Head2[N], cnt[3];
int Stack[N], top, dfn[N], low[N], Time, n, m;
int nBridge, Bridge[N];
int dist[N], vis[N], Max, index, Is[N];
struct Edge
{
    int v, next;
} e1[10*N], e2[10*N];
void Init()
{
    top = nBridge = Time = Max = index = 0;
    cnt[1] = cnt[0] = 0;
    memset(low, 0, sizeof(low));
    memset(dfn, 0, sizeof(dfn));
    memset(Bridge, 0, sizeof(Bridge));
    memset(dist, 0, sizeof(dist));
    memset(Stack, 0, sizeof(Stack));
    memset(Is, 0, sizeof(Is));
    memset(Head1, -1, sizeof(Head1));
    memset(Head2, -1, sizeof(Head2));
}
void Add(Edge e[],int Head[], int u, int v, int k)
{
    e[cnt[k]].v = v;
    e[cnt[k]].next = Head[u];
    Head[u] = cnt[k]++;
}
void Tarjar(int u, int father)
{
    low[u] = dfn[u] = ++Time;
    Stack[top++] = u;
    Is[u] = 1;
    int v, k=0;
    for(int i=Head1[u]; i!=-1; i=e1[i].next)
    {
        v = e1[i].v;
        if(v==father && !k)///避免重边;
        {
            k++;
            continue;
        }
        if(!dfn[v])
        {
            Tarjar(v, u);
            low[u] = min(low[u], low[v]);
        }
        else
            low[u] = min(low[u], dfn[v]);
    }
    if(low[u] == dfn[u])
    {
        nBridge++;///可以代表缩点后的节点个数;
        while(1)
        {
            v = Stack[--top];
            Is[v] = 0;
            Bridge[v] = nBridge;///缩点;
            if(u==v) break;
        }
    }
}
void bfs(int s)
{
    queue<int>Q;
    int p, q;
    memset(vis, 0, sizeof(vis));
    vis[s] = 1;
    dist[s] = 0;
    Q.push(s);
    while(!Q.empty())
    {
        p = Q.front(); Q.pop();
        for(int i=Head2[p]; i!=-1; i=e2[i].next)
        {
            q = e2[i].v;
            if(!vis[q])
            {
                vis[q] = 1;
                dist[q] = dist[p] + 1;
                Q.push(q);
                if(Max<dist[q])
                {
                    Max = dist[q];
                    index = q;
                }
            }
        }
    }
}
int main()
{
    int u, v;
    while(scanf("%d%d", &n, &m), m + n)///输入时由于m n弄反了,TLE的我想哭;
    {
        Init();
        for(int i=1; i<=m; i++)
        {
            scanf("%d%d", &u, &v);
            Add(e1, Head1, u, v, 0);
            Add(e1, Head1, v, u, 0);///原来的树;
        }
        Tarjar(1, 0);
        for(int i=1; i<=n; i++)
        {
            for(int j=Head1[i]; j!=-1; j=e1[j].next)
            {
                int u = Bridge[i];
                int v = Bridge[e1[j].v];
                if(u != v )
                {
                    Add(e2, Head2, u, v, 1);
                    Add(e2, Head2, v, u, 1);///缩点后的树;
                }
            }
        }
        bfs(1);
        bfs(index);///求树的直径的过程;
        printf("%d\n", nBridge-1-Max);///缩点后形成的树每条边都是桥;所以总桥的个数为节点数-1;
    }
    return 0;
}
View Code

 

 

 一年后又来写了一下,比不用扩栈也可以;

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
#include<queue>
#include<string>
#include<stack>
#include<map>
using namespace std;
#define N 205010
#define INF 0x3f3f3f3f
#define met(a, b) memset(a, b, sizeof(a))

vector<vector<int> >G;
vector<vector<int> >G1;
int low[N], dfn[N], Time;
int Block[N], nblock;
int IsStack[N], Sta[N], top;
int n, m, nbridge;

void Init()
{
    met(low, 0);
    met(dfn, 0);
    met(Block, 0);
    met(IsStack, 0);
    met(Sta, 0);
    G.clear();
    G.resize(n+1);
    G1.clear();
    G1.resize(n+1);
    Time = nblock = top = nbridge = 0;
}

void Tarjan(int u, int fa)
{
    low[u] = dfn[u] = ++Time;
    IsStack[u] = 1;
    Sta[top++] = u;
    int len = G[u].size(), v, k = 0;
    for(int i=0; i<len; i++)
    {
        v = G[u][i];
        if(v == fa && !k)
        {
            k++;
            continue;
        }
        if(!dfn[v])
        {
            Tarjan(v, u);
            low[u] = min(low[u], low[v]);

            if(low[v] > dfn[u])
                nbridge ++;
        }
        else if(IsStack[v])
        {
            low[u] = min(low[u], dfn[v]);
        }
    }
    if(low[u] == dfn[u])
    {
        ++nblock;
        do
        {
            v = Sta[--top];
            IsStack[v] = 1;
            Block[v] = nblock;
        }while(u != v);
    }
}

int vis[N], Max, Index, dist[N];

void bfs(int s)
{
    met(vis, 0);
    met(dist, 0);
    vis[s] = 1;
    queue<int> Q;
    Q.push(s);
    while(Q.size())
    {
        int p = Q.front();Q.pop();
        int len = G1[p].size(), q;
        for(int i=0; i<len; i++)
        {
            q = G1[p][i];
            if(!vis[q] && dist[q] < dist[p]+1)
            {
                dist[q] = dist[p]+1;
                if(dist[q] > Max)
                {
                    Max = dist[q];
                    Index = q;
                }
                vis[q] = 1;
                Q.push(q);
            }
        }
    }
}

int main()
{
    while(scanf("%d %d", &n, &m), m+n)
    {
        Init();

        int u, v;
        for(int i=1; i<=m; i++)
        {
            scanf("%d %d", &u, &v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        Tarjan(1, 0);
        for(int i=1; i<=n; i++)
        {
            int len = G[i].size(), v, u = Block[i];
            for(int j=0; j<len; j++)
            {
                v = Block[G[i][j]];
                if(u != v)
                {
                    G1[u].push_back(v);
                    G1[v].push_back(u);
                }
            }
        }
        Max = 0, Index = -1;
        bfs(1);
        bfs(Index);
        ///printf("%d %d\n", nbridge, Max);
        printf("%d\n", nbridge-Max);///或者nblock-1-Max;
    }
    return 0;
}
View Code

 

posted @ 2015-08-14 11:50  西瓜不懂柠檬的酸  Views(232)  Comments(0Edit  收藏  举报
levels of contents