现在的位置: 首页 > 综合 > 正文

[HDU 4118]Holiday’s Accommodation[图论][非递归dfs]

2017年12月13日 ⁄ 综合 ⁄ 共 2380字 ⁄ 字号 评论关闭

题意:

有一棵树, 节点表示城市, 每个城市有一个人. 人们要旅行到其他城市. 相当于一个置换. 求总的路程最大值.

思路:

ans = Σ (每条路长 l )*(经过这条路的最大次数 f )

f = 2 * 这条边左边节点数和右边节点数最小值k. (这样左边的每一个点一定能够对应右边的某个点)

这个k可以dfs 求得.

但是直接dfs 会爆栈...

需要将其改为非递归...

#include <stdio.h>
#include <cstring>
#include <stack>
using namespace std;
struct gtype
{
    int y,d,next;
} g[200010];
int first[100100],tot,a[100100],tt,x,y,d,n;
bool v[100100];
inline int min(int a,int b)
{
    if (a<b) return a;
    else return b;
}
void add(int x,int y,int d)
{
    tot++;
    g[tot].d=d;
    g[tot].y=y;
    g[tot].next=first[x];
    first[x]=tot;
    tot++;
    g[tot].d=d;
    g[tot].y=x;
    g[tot].next=first[y];
    first[y]=tot;
}
void dfs(int p)
{
    stack <int> stk;
    int t=first[p];

    stk.push(p);
    v[p]=true;
    while (1)
    {
        if (t==-1)//该点的所有边已经遍历完
        {
            int y = stk.top();
            stk.pop();
            if (stk.empty()) break;//dfs结束
            a[stk.top()]+=a[y];
            //该点的子树节点数加到其父节点上, 表示该节点相对其父节点已经处理完
        }
        int x = stk.top();///每次从栈顶取出元素!!
        t = first[x];
        while (t!=-1)
        {
            if (!v[g[t].y])
            {
                v[g[t].y]=true;
                stk.push(g[t].y);
                break;///一旦前进,便跳出,继续判断或者取栈顶元素.否则就相当于bfs了!!!
            }
            t=g[t].next;
        }
    }
}
int main()
{
    scanf("%d",&tt);
    for (int cas=1; cas<=tt; cas++)
    {
        tot=0;
        memset(g,0,sizeof(g));
        memset(first,-1,sizeof(first));
        scanf("%d",&n);
        for (int i=1; i<n; i++)
        {
            scanf("%d%d%d",&x,&y,&d);
            add(x,y,d);
        }
        for (int i=1; i<=n; i++) a[i]=1;//记录节点的儿子数,初始化时已经算上了自己
        memset(v,0,sizeof(v));
        dfs(1);//填a数组
        __int64 ans=0;
        for (int i=1; i<=2*(n-1); i+=2)
        {//遍历池子,即遍历每一条边
            x=g[i].y;
            y=g[i+1].y;
            d=min(a[x],a[y]);
            ans+=min(d,n-d)*2*g[i].d;
        }
        printf("Case #%d: %I64d\n",cas,ans);
    }
    return 0;
}

自己敲一遍. 尤其要感受一下是如何改非递归的!

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stack>
using namespace std;
const int MAXN = 100005;
typedef long long ll;
struct edge
{
    int v, w, next;
}g[MAXN<<1];
int head[MAXN],num;
int a[MAXN];
bool vis[MAXN];
int n;
stack<int> s;
void add(int u, int v, int w)
{
    g[++num].v = v;
    g[num].w = w;
    g[num].next = head[u];
    head[u] = num;
    g[++num].v = u;
    g[num].w = w;
    g[num].next = head[v];
    head[v] = num;
}

void dfs(int k)
{
    memset(vis,false,sizeof(vis));
    vis[k] = true;
    s.push(k);
    int t = head[k];

    while(true)
    {
        if(!t)
        {
            int u = s.top();
            s.pop();//deal with tmp node
            if(s.empty())   break;
            a[s.top()] += a[u];
        }

        int u = s.top();
        t = head[u];
        while(t)
        {
            if(!vis[g[t].v])
            {
                vis[g[t].v] = true;
                s.push(g[t].v);
                break;
            }
            t = g[t].next;
        }
    }
}

int main()
{
    int T,cas = 0;
    scanf("%d",&T);
    while(T--)
    {
        num = 0;
        memset(head,0,sizeof(head));
        memset(g,0,sizeof(g));
        scanf("%d",&n);
        for(int i=1;i<n;i++)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            add(u,v,w);
        }
        for(int i=1;i<=n;i++)   a[i] = 1;

        dfs(1);
        ll ans = 0;
        for(int i=1;i<2*(n-1);i+=2)
        {
            int u = g[i].v;
            int v = g[i+1].v;
            int k = min(a[u],a[v]);//a记录子树大小,较小的那个是"一边的节点数"
            ans += 2ll*min(k,n-k)*g[i].w;//n-k是对面的个数
        }
        printf("Case #%d: %I64d\n",++cas,ans);
    }
}

/* 自律 专注 保持活力 */

抱歉!评论已关闭.