Hrbust 1329 Leyni, 罗莉 与 游乐场【树型Dp+思维】好题!

351 篇文章 2 订阅
224 篇文章 2 订阅

Leyni, 罗莉 与 游乐场
Time Limit: 2000 MSMemory Limit: 65536 K
Total Submit: 55(16 users)Total Accepted: 20(13 users)Rating: Special Judge: No
Description

Leyni喜欢和罗莉们玩,他认识n个罗莉,这些罗莉喜欢去游乐场玩,Leyni正在计划为她们建立一些游乐场,进而让每一个人都能去游乐场玩。

这些罗莉都分布在不同的城市,而且这些城市中,任意两个城市间有且只有一种路线(这个路线可能需要经过若干条城市之间的公路),每条城市之间的公路的长度都相等(都是1单位长度)。

现在这些城市都没有游乐场,Leyni要选择某些城市来建立游乐场,但是建立一个游乐场将花费k元。这样,对于有游乐场的城市中的罗莉将能不支付任何费用去游乐场玩;对于不建立游乐场的城市中的罗莉,Leyni将安排她去一个有游乐场的城市,并替她支付路费dlen元(其中len为该罗莉所在城市到指定城市的距离)

Leyni想知道,为了让每个罗莉都能去游乐场玩,他最少要花费多少元?

Input

本题有多组测试数据,输入的第一行是一个整数T代表着测试数据的数量,接下来是T组测试数据。

对于每组测试数据:

1 包含两个整数nk (1 ≤ n ≤ 200, 1 ≤ k ≤ 105)

2 包含n – 1个整数di,下标从1开始 (di ≤ di + 1, 0 ≤ di ≤ 105)

接下来n – 1 每行包含了一对整数uv,表示城市u和城市v之间存在一条公路(城市的下标从1开始)。

Output

对于每组测试数据:

1 输出Leyni所需要的最小花费。

Sample Input

1

8 10

2 5 9 11 15 19 20

1 4

1 3

1 7

4 6

2 8

2 3

3 5

Sample Output

38

Hint

对于样例,可以选择在城市3和城市4建立两个游乐场,并安排城市12578的罗莉去城市3玩,安排城市6的罗莉去城市4玩。

Source
哈理工2012春季校赛热身赛 2012.04.03
Author
齐达拉图@HRBUST

思路:


1、首先我们O(n^2)的去预处理出树上两点间距离。


2、因为涉及到最小花费,而且保证图是一棵树,那么我们肯定要想树型dp.


我们设定Dp【i】【 j】表示城市i的萝莉都到城市j去玩的最小花费。


那么不难推出其状态转移方程:Dp【u】【i】= d【dist[u][i]】+Σmin(Dp【v】【i】,min(Dp[v][j])+K);


3、那么在树上进行dp的过程维护一下即可。


其实这个问题还可以进行一波优化,大体的思路就是将状态转移方程改为:Dp【u】【i】= d【dist[u][i]】+Σmin(Dp【v】【i】,opt【i】+K);

这里opt【i】其实就是min(Dp【v】【j】)的最小值的点的编号。

所以这波可以优化一下。


没优化暴力去Dp的话是800+ms.优化之后是30+ms.........................


Ac代码(没优化的800+ms):

#include<stdio.h>
#include<string.h>
#include<vector>
using namespace std;
vector<int >mp[5000];
int dist[300][300];
int dp[300][300];
int d[5000];
int n,kk;
void dfs(int ss,int u,int from,int depth)
{
    dist[ss][u]=depth;
    for(int i=0;i<mp[u].size();i++)
    {
        int v=mp[u][i];
        if(v==from)continue;
        else
        {
            dfs(ss,v,u,depth+1);
        }
    }
}
int Dfs(int u,int from)
{
    for(int i=0;i<mp[u].size();i++)
    {
        int v=mp[u][i];
        if(v==from)continue;
        Dfs(v,u);
    }
    for(int i=1;i<=n;i++)
    {
        dp[u][i]=d[dist[u][i]];
        for(int j=0;j<mp[u].size();j++)
        {
            int minn=0x3f3f3f3f;
            int v=mp[u][j];
            for(int k=1;k<=n;k++)
            {
                minn=min(dp[v][k],minn);
            }
            dp[u][i]+=min(dp[v][i],minn+kk);
        }
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&kk);
        memset(dp,0,sizeof(dp));
        memset(dist,0,sizeof(dist));
        memset(d,0,sizeof(d));
        for(int i=1;i<=n;i++)mp[i].clear();
        for(int i=1;i<=n-1;i++)
        {
            scanf("%d",&d[i]);
        }
        for(int i=1;i<=n-1;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            mp[x].push_back(y);
            mp[y].push_back(x);
        }
        for(int i=1;i<=n;i++)dfs(i,i,-1,0);
        Dfs(1,-1);
        int output=0x3f3f3f3f;
        for(int i=1;i<=n;i++)
        {
            output=min(dp[1][i]+kk,output);
        }
        printf("%d\n",output);
    }
}


Ac代码2(优化之后的32ms):


#include<stdio.h>
#include<string.h>
#include<vector>
using namespace std;
vector<int >mp[5000];
int dist[300][300];
int dp[300][300];
int opt[300];
int d[5000];
int n,kk;
void dfs(int ss,int u,int from,int depth)
{
    dist[ss][u]=depth;
    for(int i=0;i<mp[u].size();i++)
    {
        int v=mp[u][i];
        if(v==from)continue;
        else
        {
            dfs(ss,v,u,depth+1);
        }
    }
}
void Dfs(int u,int from)
{
    for(int i=0;i<mp[u].size();i++)
    {
        int v=mp[u][i];
        if(v==from)continue;
        Dfs(v,u);
    }
    int tmp=0x3f3f3f3f;
    for(int i=1;i<=n;i++)
    {
        dp[u][i]=d[dist[u][i]];
        for(int j=0;j<mp[u].size();j++)
        {
            int v=mp[u][j];
            if(opt[v])
            {
                dp[u][i]+=min(dp[v][i],dp[v][opt[v]]+kk);
            }
        }
        if(dp[u][i]<tmp)
        {
            opt[u]=i;
            tmp=dp[u][i];
        }
    }
    return ;
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&kk);
        memset(opt,0,sizeof(opt));
        memset(dist,0,sizeof(dist));
        memset(d,0,sizeof(d));
        for(int i=1;i<=n;i++)mp[i].clear();
        for(int i=1;i<=n-1;i++)
        {
            scanf("%d",&d[i]);
        }
        for(int i=1;i<=n-1;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            mp[x].push_back(y);
            mp[y].push_back(x);
        }
        for(int i=1;i<=n;i++)dfs(i,i,-1,0);
        Dfs(1,-1);
        printf("%d\n",dp[1][opt[1]]+kk);
    }
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值