卧薪尝胆,厚积薄发。
HNOI2018 道路
Date: Sat Sep 29 18:58:22 CST 2018 In Category: NoCategory

Description:

一个国家有 $n-1$ 个城市和 $n$ 个乡村构成一棵树,乡村是叶子,每个城市最多有 $2$ 个出边,一个公路一个铁路,可以翻新每个城市的一条入边,第 $i$ 个乡村的代价为 $c_i\times (a_i+从i到根未翻修的公路个数)\times(b_i+从i到根未翻修的铁路个数)$ ,最小化总代价。
$1\leqslant n \leqslant 20000$ ,保证每个乡村到根只有不超过 $40$ 条路。

Solution:

这个题运用了动态规划的一个重要思想,就是先预先对未来的决策做出一些承诺,然后在状态里把这个承诺记下来,让以后的转移去实现。
设 $f[k][i][j]$ 表示从根到 $k$ 未翻修的公路有 $i$ 个,铁路有 $j$ 个的最小代价,既然从每个乡村到根只有不到 $40$ 条路,那么完全可以开一个 $f[20000][40][40]$ 把它记下来,最后的答案就是 $f[1][0][0]$ ,转移为 $f[k][i][j]=\min(f[s[k]][i][j]+f[t[k]][i][j+1],f[s[k]][i][j+1]+f[t[k]][i][j])$ ,记忆化搜索比较容易写。

Code:


#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
int n;
#define MAXN 20010
typedef long long ll;
ll s[MAXN],t[MAXN];
ll a[MAXN],b[MAXN],c[MAXN];
ll f[MAXN][41][41];
ll dp(int k,int x,int y)
{
if(k < 0)
{
int t = -k;
return c[t] * (a[t] + x) * (b[t] + y);
}
if(f[k][x][y] != -1)return f[k][x][y];
return f[k][x][y] = min(dp(s[k],x,y) + dp(t[k],x,y + 1),dp(s[k],x + 1,y) + dp(t[k],x,y));
}
int main()
{
memset(f,-1,sizeof(f));
scanf("%d",&n);
for(int i = 1;i <= n - 1;++i)
{
scanf("%d%d",&s[i],&t[i]);
}
for(int i = 1;i <= n;++i)
{
scanf("%d%d%d",&a[i],&b[i],&c[i]);
}
cout << dp(1,0,0) << endl;
return 0;
}
In tag: DP-树形DP
Copyright © 2020 wjh15101051
ღゝ◡╹)ノ♡