[BZOJ4711]小奇挖矿:树形DP

分析:

想了半天该怎么将运输的代价累计进去,其实直接设为状态的初值即可,建仓库的代价在转移时计算即可。
于是状态设计和转移也显然,f[i][j]表示已经讨论完以i为根的子树,其中需要向外运输的会运输到j。
转移为f[i][j]=d[dis[i][j]]+∑min(f[child[i]][j],f[child[i]][j']+k)。

代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <cmath>
#include <algorithm>
typedef long long LL;

int n,kk,ecnt,d[205],head[205],f[205][205];
int dis[205][205],now;
struct Edge{
    int to,nxt;
}e[405];

inline void add_edge(int bg,int ed){
    ecnt++;
    e[ecnt].to=ed;
    e[ecnt].nxt=head[bg];
    head[bg]=ecnt;
}

void dfs1(int x,int pre,int len){
    dis[now][x]=d[len];
    for(int i=head[x];i;i=e[i].nxt){
        int ver=e[i].to;
        if(ver==pre) continue;
        dfs1(ver,x,len+1);
    }
}

void dfs2(int x,int pre){
    for(int i=1;i<=n;i++) f[x][i]=dis[x][i];
    for(int i=head[x];i;i=e[i].nxt){
        int ver=e[i].to;
        if(ver==pre) continue;
        dfs2(ver,x);
        for(int j=1;j<=n;j++){
            int temp=f[ver][j];
            for(int k=1;k<=n;k++)
                temp=std::min(temp,f[ver][k]+kk);
            f[x][j]+=temp;
        }
    }
}

int main(){
    scanf("%d%d",&n,&kk);
    for(int i=1;i<n;i++) scanf("%d",&d[i]);
    for(int i=1;i<n;i++){
        int u,v;scanf("%d%d",&u,&v);
        add_edge(u,v);
        add_edge(v,u);
    }
    for(int i=1;i<=n;i++) now=i,dfs1(i,0,0);
    memset(f,0x3f,sizeof f);
    dfs2(1,0);
    int ans=0x3f3f3f3f;
    for(int i=1;i<=n;i++) ans=std::min(ans,f[1][i]+kk);
    printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/ErkkiErkko/p/9756802.html