POJ-2152 树形dp

题意 给你一颗树,边上权值表示距离,一个点上可以建消防站,花费为val[i],一个节点要么建消防站,要么周围d[i]距离内必须有一个消防站,求最小花费。
想了好久没想到,看了一眼题解才恍然大悟。我真的是太菜了!
题解:设ans[u]为以u为根的这棵子树的答案,dp[u][v]表示u节点被v保护的答案,dis[v]表示u到v的距离。若v可以保护u,则
dp[u][v]=val[v]+∑min(dp[i][v]−val[v],ans[i]);//其中i是u的直接子节点
ans[u]=min{dp[u][i]};

代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cmath>
using namespace std;
typedef long long ll;
const int maxn=1010;
int dp[maxn][maxn],dis[maxn],val[maxn],d[maxn],ans[maxn];
struct node{
	int x;
	int w;
	node(int _x,int _w)
	{
		x=_x;
		w=_w;
	}
};
vector<node>a[maxn];
int t,n;
void Dfs(int u,int fa)
{
	for(int j=0;j<a[u].size();j++)
	{
		int v=a[u][j].x;
		ll w=a[u][j].w;
		if(v==fa) continue;
		dis[v]=dis[u]+w;
		Dfs(v,u);
	}
}
void dfs(int u,int fa)
{
	dp[u][u]=val[u];
	for(int j=0;j<a[u].size();j++)
	{
		int v=a[u][j].x;
		ll w=a[u][j].w;
		if(v==fa) continue;
		dfs(v,u);
	}
	dis[u]=0;
	Dfs(u,-1);
	for(int i=1;i<=n;i++)
		if(dis[i]<=d[u])///能被覆盖到
		{
			dp[u][i]=val[i];
			for(int j=0;j<a[u].size();j++)
			{
				int v=a[u][j].x;
				if(v==fa) continue;
				dp[u][i]+=min(ans[v],dp[v][i]-val[i]);///
			}
			ans[u]=min(ans[u],dp[u][i]);///
		}
}
int main()
{
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&n);
		for(int i=1;i<=n;i++)
		scanf("%d",&val[i]);
		for(int i=1;i<=n;i++)
		scanf("%d",&d[i]);
		
		for(int i=1;i<=n;i++)
		a[i].clear();
		memset(dp,0x3f,sizeof dp);
		memset(ans,0x3f,sizeof ans);
		for(int i=1;i<n;i++)
		{
			int x,y,w;
			scanf("%d%d%d",&x,&y,&w);
			a[x].push_back(node(y,w));
			a[y].push_back(node(x,w));
		}
		dfs(1,-1);
		 
		printf("%d\n",ans[1]);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/dllpxfire/article/details/80938608