[HNOI/AHOI2018]道路,第三次模拟赛,树形DP

正题

      [HNOI/AHOI2018]道路

      这题想了半天打了个贪心一点分都拿不到...

      其实这一题看到“任意乡村可以通过不超过40条道路到达首都。”所以说这个题的深度最大是40层,也就是说,每个乡村路径上的铁路和公路的总和一共也就40条。

      所以我们要猜想它的时间复杂度。

      20000*40*40=32000000.实际上是没有那么多的。

      记忆化搜索就是我们所谓的树形DP。

      我们想想,一个节点的两条边是不是只和子树内的叶子结点有关系,而跟外面的叶子结点没有关系。

      那么我们用f[i][j][k]来表示第i个点,在上面未翻修j条公路和k条铁路后产生的最小价值。

      f[i][j][k]显然等于选左边翻修,和选右边翻修的最小值。

      反过来那么就是右边不翻修,左边不翻修。

      所以,把状态不断往下传递求最小值即可。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;

int n;
long long a[20010],b[20010],c[20010];
int son[40010][2];
long long f[30010][41][41];

long long dfs(int x,int y,int z){
	if(son[x][0]==0) return c[x-(n-1)]*(a[x-(n-1)]+y)*(b[x-(n-1)]+z);
	if(f[x][y][z]!=f[20009][40][40]) return f[x][y][z];
	return f[x][y][z]=min(dfs(son[x][0],y+1,z)+dfs(son[x][1],y,z),dfs(son[x][0],y,z)+dfs(son[x][1],y,z+1));
}

int main(){
//	freopen("road.in","r",stdin);
//	freopen("road.out","w",stdout);
	scanf("%d",&n);
	memset(f,63,sizeof(f));
	for(int i=1;i<=n-1;i++){
		int x,y;
		scanf("%d %d",&x,&y);
		if(x>0) son[i][0]=x;
		else son[i][0]=-x+n-1;
		if(y>0) son[i][1]=y;
		else son[i][1]=-y+n-1;
	}
	for(int i=1;i<=n;i++) scanf("%lld %lld %lld",&a[i],&b[i],&c[i]);
	printf("%lld",dfs(1,0,0));
}

猜你喜欢

转载自blog.csdn.net/deep_kevin/article/details/80058994