HLG 1329 Leyni, 罗莉 与 游乐场【树形DP】

题意: 给一个树形的图,可以在任意一个位置建造游乐场,每个游乐场都有相应的造价,如果该节点建有游乐场,那么改点的人到该游乐场就不需要花费,

    否则需要花费所在位置到游乐场的路径长度对应的费用,问怎么建游乐场能使得总花费最小。

分析: 树形DP。

    用 f[i][j] 表示以 i 为根节点的子树以 j 为游乐场的最小花费,为了便于状态转移,这个游乐

    场先不计费,f[i][j]  中的最优的 j 一定是其某个子节点,这样就长生了最优子结构,所以计算

    f[i][j]时  ,f[i][j]  的值可以由其子节点来确定,状态转移方程如下:

    f[i][j]=d[len[i][j]]+sum( min( f[c][j],f[c][opt[c]]+k) )

    其中   c 是 i 的孩子。即考察每个子树的根以 j 为游乐场(这样能够省下 K 元),要么就是不 省  

    k 元而使用opt[c]  为游乐场,由于子树外的点一定不会是该子树的最优解, 所以 I 的各个子树的

           opt[c]一定不同,这里直接加 K 。

f[根][opt[根]]+K就是答案。

code :  

View Code
#include<stdio.h>
#include<string.h>
#define INF 0x1f1f1f1f
#define min(a,b)(a)<(b)?(a):(b)
int n,p;
int g[202][202];
int d[202];
int v[202];
int opt[202];
int f[202][202];
int tot=0;
void dfs(int r)
{
    v[r]=1;
    int i,j,tmp,sum,flag=0;
    for(i=1;i<=n;i++)
        if(g[r][i]==1&&!v[i])
        {
            flag=1;             
            dfs(i);
        }
    if(flag==0)                   // 如果是叶子节点
    {
        for(i=1;i<=n;i++)          
            f[r][i]=d[g[r][i]];   // 如果游乐场选在树外的话,f[r][i]为这个叶子节点到i 的距离
        opt[r]=r;                 // 应为当选择了游乐场之后该游乐场不计费,所以当
        return;                   // 当树中只存在一个节点的时候造价为f[i][opt[i]]+k
    }                             // 其中 opt[i]=i 即f[i][opt[i]]=0 
    tmp=INF;
    for(i=1;i<=n;i++)             // 如果不是叶子节点
    {
        f[r][i]=d[g[r][i]];       // 费用要先包含根到游乐场的路程的费用
        for(j=1;j<=n;j++)         
            if(g[r][j]==1&&opt[j])// 如果j 是r 的直接后代,并且opt[j]已有值,即J已经选择好了
                f[r][i]+=min(f[j][i],f[j][opt[j]]+p);// 游乐场,也说明 j 不是 r 的祖先 
        if(f[r][i]<tmp)          
        {                         // 找出建造费用最少的做为opt[r]
            opt[r]=i;
            tmp=f[r][i];
        }
        
    }
}
int main()
{
//    freopen("D:ce.txt","r",stdin);
    int i,j,k,t,a,b;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&p);
        d[0]=0;
        for(i=1;i<n;i++)
            scanf("%d",&d[i]);
        memset(v,0,sizeof(v));
        memset(g,0,sizeof(g));
        memset(opt,0,sizeof(opt));
        memset(f,0,sizeof(f));
        for(i=1;i<n;i++)
        {
            scanf("%d%d",&a,&b);
            g[a][b]=g[b][a]=1;
        }
        for(k=1;k<=n;k++)         // Floyed求任意两点间的最短距离
            for(i=1;i<=n;i++)    
                if(g[i][k])
                    for(j=1;j<=n;j++)
                    {
                        if(j==i)continue;
                        if(g[k][j]&&(g[i][k]+g[k][j]<g[i][j]||g[i][j]==0))
                            g[i][j]=g[i][k]+g[k][j];
                    }
        dfs(1);
        printf("%d\n",f[1][opt[1]]+p);   
    }
    return 0;
}

 

    

转载于:https://www.cnblogs.com/dream-wind/archive/2012/05/30/2527111.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值