这是一道树上的dp问题,题意是给你一棵满的二叉树(父亲表示法),然后左叉为公路,右叉为铁路,每个非叶子节点都是一个城市,每个叶子节点都是一个乡村,让你翻修n-1条路,每个乡村有一个不便利值,这个值和这个乡村上面没翻修的公路数和铁路数有关,让你求最小不便利值的和。
因为树本身的父亲和儿子就有一种关系,所以常用这种关系从父亲递推到儿子。
考虑dp先考虑让做什么事:就是选择一些边使最后权值和最小。
然后考虑状态,一般树上dp都有一维为当前节点i,然后考虑因为我最后到叶子节点时我需要用未翻修的路的数目来求值,所以应该还有两个状态为这条路上翻修了多少条公路多少条铁路,所以状态确定为 f [ i ][ x ][ y ] 表示当前节点i上面未修x条公路,y条铁路。
为何判断出为i上面的呢?因为如果设为i下面的,我在走到i的时候下面修了多少条路我不知道,而且这也不利于到叶子节点时计算值。
最后就是转移,f [ i ][ x ][ y ] = min{f [ l [ i ] ][ x+1 ][ y ] + f [ r[ i ] ][ x ][ y ] , f [ l[ i ] ][ x ][ y ] + f[ r[ i ] ][ x ][ y+1 ] }
第一个决策是选择修铁路,那么左儿子会因此多了一条没翻修的公路,第二个决策是选择修公路,那么右儿子会因此多了一条没翻修的铁路。由于为树形结构,可以写一个记忆化,会好受一点。
#include<iostream>
#include<cstdio>
using namespace std;
int n;
int l[20005],r[20005];
long long a[40010],b[40010],c[40010];
long long f[20005][45][45];
long long dfs(int i,int x,int y)
{
if(i>=n) return c[i]*(a[i]+x)*(b[i]+y);
if(f[i][x][y]) return f[i][x][y];
f[i][x][y]=min(dfs(l[i],x+1,y)+dfs(r[i],x,y),dfs(l[i],x,y)+dfs(r[i],x,y+1));
return f[i][x][y];
}
void getdata()
{
scanf("%d",&n);
int s,t;
for(int i=1;i<n;i++)
{
scanf("%d%d",&s,&t);
l[i]=s>0?s:n-1-s;
r[i]=t>0?t:n-1-t;
}
for(int i=n;i<n*2;i++)
{
scanf("%d%d%d",&a[i],&b[i],&c[i]);
}
printf("%lld",dfs(1,0,0));
}
int main()
{
getdata();
}