[Hnoi2018]道路——树形DP

Description:

W 国的交通呈一棵树的形状。W 国一共有 n 1 个城市和 n 个乡村,其中城市从 1 n 1 编号,乡村从 1 n 编号,且 1 号城市是首都。道路都是单向的,本题中我们只考虑从乡村通往首都的道路网络。对于每一个城市,恰有一条公路和一条铁路通向这座城市。对于城市i, 通向该城市的道路(公路或铁路)的起点,要么是一个乡村,要么是一个编号比 i 大的城市。 没有道路通向任何乡村。除了首都以外,从任何城市或乡村出发只有一条道路;首都没有往 外的道路。从任何乡村出发,沿着唯一往外的道路走,总可以到达首都。

W 国的国王小 W 获得了一笔资金,他决定用这笔资金来改善交通。由于资金有限,小 W 只能翻修 n 1 条道路。小 W 决定对每个城市翻修恰好一条通向它的道路,即从公路和铁 路中选择一条并进行翻修。小 W 希望从乡村通向城市可以尽可能地便利,于是根据人口调 查的数据,小 W 对每个乡村制定了三个参数,编号为 i 的乡村的三个参数是 a i ​, b i ​ 和 c i 。假设 从编号为 i 的乡村走到首都一共需要经过 x 条未翻修的公路与 y 条未翻修的铁路,那么该乡村 的不便利值为

c i ( a i + x ) ( b i + y )

在给定的翻修方案下,每个乡村的不便利值相加的和为该翻修方案的不便利值。 翻修 n 1 条道路有很多方案,其中不便利值最小的方案称为最优翻修方案,小 W 自然 希望找到最优翻修方案,请你帮助他求出这个最优翻修方案的不便利值。

思路:

大家都说这题是一道普及DP,但也没有看到有很多人在考场A啊。所以这种题还是要引起我们的注意。
考虑树形DP,考虑对与每一颗子树,我们求它的所有的叶子节点的最小权值。由于这是一颗二叉树,所以我们可以从两个子节点转移到父节点,即对于每一个父节点枚举修建哪一条边。但是还要使得我们的选择是合法的,即对于左儿子和右儿子的最小的权值和的情况满足从他们到根节点的情况一致。所以我们可以设置DP的状态了,即 d p i , j , k 表示第 i 个节点以上的路径里有 i 未修建的公路和 j 条未修建的铁路。转移方程就很好地出来了。

d p u , j , k = min ( d p l c , j , k + d p r c , j , k + 1 , d p l c , j + 1 , k + d p r c , j , k )

另外,好像要用滚动数组卡一下空间。。。

/*========================
 * Auhtor : ylsoi
 * Problem : road
 * Algorithm : DP
 * Time : 2018.4.24
 * =====================*/
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
void File(){
    freopen("road.in","r",stdin);
    freopen("road.out","w",stdout);
}
#define REP(i,a,b) for(register int i=a;i<=b;++i)
#define DREP(i,a,b) for(register int i=a;i>=b;--i)
#define MREP(i,x) for(register int i=beg[x];i;i=E[i].last)
#define ll long long
const int maxn=4e4+1;
const int maxd=40+1;
int n,ch[maxn][2],a[maxn],b[maxn],c[maxn],cnt,be[maxn];
ll dp[maxn/2+1][maxd][maxd];
#define lc ch[u][0]
#define rc ch[u][1]
ll _min(ll x,ll y){return x<y ? x : y;}
void dfs(int u,int cnt0,int cnt1){
    if(!lc && !rc){
        REP(i,0,cnt0)REP(j,0,cnt1)
            dp[be[u]][i][j]=(ll)c[u]*(a[u]+i)*(b[u]+j);
        return;
    }
    be[lc]=++cnt;be[rc]=++cnt;
    dfs(lc,cnt0+1,cnt1);
    dfs(rc,cnt0,cnt1+1);
    REP(j,0,cnt0)REP(k,0,cnt1)
        dp[be[u]][j][k]=_min(dp[be[lc]][j][k]+dp[be[rc]][j][k+1],dp[be[lc]][j+1][k]+dp[be[rc]][j][k]);
    cnt-=2;
}
int main(){
    File();
    scanf("%d",&n);
    REP(i,1,n-1){
        int s,t;
        scanf("%d%d",&s,&t);
        if(s>0)ch[i][0]=s;
        else ch[i][0]=-s+n-1;
        if(t>0)ch[i][1]=t;
        else ch[i][1]=-t+n-1;
    }
    REP(i,1,n)scanf("%d%d%d",&a[i+n-1],&b[i+n-1],&c[i+n-1]);
    be[1]=++cnt;
    dfs(1,0,0);
    printf("%lld\n",dp[1][0][0]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/ylsoi/article/details/80085081