hdu4118 树形dp

25 篇文章 0 订阅

http://acm.hdu.edu.cn/showproblem.php?pid=4118

Problem Description
Nowadays, people have many ways to save money on accommodation when they are on vacation.
One of these ways is exchanging houses with other people.
Here is a group of N people who want to travel around the world. They live in different cities, so they can travel to some other people's city and use someone's house temporary. Now they want to make a plan that choose a destination for each person. There are 2 rules should be satisfied:
1. All the people should go to one of the other people's city.
2. Two of them never go to the same city, because they are not willing to share a house.
They want to maximize the sum of all people's travel distance. The travel distance of a person is the distance between the city he lives in and the city he travels to. These N cities have N - 1 highways connecting them. The travelers always choose the shortest path when traveling.
Given the highways' information, it is your job to find the best plan, that maximum the total travel distance of all people.
 

Input
The first line of input contains one integer T(1 <= T <= 10), indicating the number of test cases.
Each test case contains several lines.
The first line contains an integer N(2 <= N <= 10 5), representing the number of cities.
Then the followingN-1 lines each contains three integersX, Y,Z(1 <= X, Y <= N, 1 <= Z <= 10 6), means that there is a highway between city X and city Y , and length of that highway.
You can assume all the cities are connected and the highways are bi-directional.
 

Output
For each test case in the input, print one line: "Case #X: Y", where X is the test case number (starting with 1) and Y represents the largest total travel distance of all people.
 

Sample Input
  
  
2 4 1 2 3 2 3 2 4 3 2 6 1 2 3 2 3 4 2 4 1 4 5 8 5 6 5
 

Sample Output
  
  
Case #1: 18 Case #2: 62

/**
hdu 4118 树形dp
题目大意:一棵n节点的树,每个节点有一个人,每个人离开自己的位置到另一个位置,每个位置只能有一个人,问这n个人移动距离和的最大值。
解题思路:(转)如果找出树的重心就可以把树分成左右两部分,左边的点跟右边的点交换位置,两点交换位置的距离=两点到根节点的距离之和的2倍,
           总距离就是所有点到根节点距离之和的2倍了,当时犹豫了一下,如果节点数是奇数的话,这样是不是根节点没换位置呢?后来一想,
           这种交换位置每个点走的路径都经过根节点,根节点跟任意一个点交换一下就可以了,答案还是一样的。
           树形DP:我们可以统计没条边被走了多少次,一条边可以把点分为左右两部分,两部分中点数较少的一部分都要离开自己的位置去另一边,
           这条边被走了min(son[u](右边点数),son[v])*2次,点数多的一部分有的位置是没变的,但是我们这样把每条边都操作一遍后,所有点的位置都变了。
注:递归dfs会爆内存。
*/
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>
using namespace std;
typedef long long LL;
const int maxn=100005;

struct note
{
    int v,w,next;
} edge[maxn*2];

int head[maxn],ip;
int n,vis[maxn],num[maxn],sta[maxn];
LL ans;

void init()
{
    memset(head,-1,sizeof(head));
    ip=0;
}

void addedge(int u,int v,int w)
{
    edge[ip].v=v,edge[ip].w=w,edge[ip].next=head[u],head[u]=ip++;
}

///递归形式会超出内存
void dfs(int u,int pre)
{
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].v;
        if(v==pre)continue;
        dfs(v,u);
        num[u]+=num[v];
        ans+=(LL)edge[i].w*2*min(num[v],n-num[v]);
    }
    num[u]++;
}

///用栈实现非递归形式
void dfs1(int u)
{
    memset(vis,0,sizeof(vis));
    int top=0;
    sta[top++]=u;
    vis[u]=1;
    while(top>0)
    {
        bool flag=1;
        int t=sta[top-1];
        for(int i=head[t]; i!=-1; i=edge[i].next)
        {
            int v=edge[i].v;
            if(vis[v])continue;
            sta[top++]=v;
            vis[v]=1;
            flag=0;
        }
        if(flag==0)continue;
        for(int i=head[t]; i!=-1; i=edge[i].next)
        {
            int v=edge[i].v;
            if(num[v]!=0)
            {
                num[t]+=num[v];
                ans+=(LL)edge[i].w*2*min(num[v],n-num[v]);
            }
        }
        num[t]++;
        top--;
    }
}

int main()
{
    int T,tt=0;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        init();
        for(int i=1; i<n; i++)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            addedge(u,v,w);
            addedge(v,u,w);
        }
        ans=0;
        memset(num,0,sizeof(num));
        ///dfs1(1);
        dfs(1,-1);
        printf("Case #%d: %I64d\n",++tt,ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值