题目链接;https://vjudge.net/contest/246776#problem/C
题目大意:有n个城市,每两个城市只有一条路连接,(所以可以当成是一颗树)。现在要建设一些防火站,使每个城市都可以被覆盖。每个城市的属性是:在本城市建防火站的价钱和不在本城市建设的话,所依赖的别的防火站的最长距离。然后是n-1条路,和这条路的长度。现在问在覆盖所有城市的前提下的最少价格。
题目思路:
dp[i][j]:表示以i为根的子树里修建一些消防站,并在节点j处修建一消防站,i的负责站必须是j;
best[i]:表示表示以i为根的子树的所有节点都有负责站的最小花费。
dist[i]:表示i到x的距离(每换换一个节点,都要再求一次dist[])。
状态状态转移方程:dp[i][j]=w[j]+sum(min(best[y],dp[y][j]-w[j]))(y是i的所有孩子,除了父节点);
解释:对于结点i,如果它使用j结点的防火站。那么对于他的孩子的子树而言,要么加上该孩子v的以v为根的子树的所有节点都有负责站的最小花费,即best【v】,要么加上dp[y][j],同样是以j为防火站,因为此时i已经加上了j防火站建造的费用,所以dp【y】【j】可以减去w【j】的费用。两者取个小的加上即可。
#include <iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<string>
#include<vector>
using namespace std;
const int maxn=1010;
const int inf=1<<30;
struct City
{
int d,w;
}city[maxn];
struct edge
{
int child,d;
edge(int x,int y)
{
child=x;
d=y;
}
};
vector <edge>tree[maxn];
int n,dis[maxn],best[maxn],dp[maxn][maxn];
void init()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)tree[i].clear();
for(int i=1;i<=n;i++)scanf("%d",&city[i].w);
for(int i=1;i<=n;i++)scanf("%d",&city[i].d);
for(int i=1;i<n;i++)
{
int l,r,c;
scanf("%d%d%d",&l,&r,&c);
tree[l].push_back(edge(r,c));
tree[r].push_back(edge(l,c));
}
}
void getdis(int key)
{
int len=tree[key].size();
for(int i=0;i<len;i++)
{
int v=tree[key][i].child;
if(dis[v]!=-1)continue;
dis[v]=dis[key]+tree[key][i].d;
getdis(v);
}
}
void dfs(int key,int parent)
{
int len=tree[key].size();
for(int i=0;i<len;i++)
{
int v=tree[key][i].child;
if(v!=parent)
dfs(v,key);
}
memset(dis,-1,sizeof dis);
best[key]=inf;dis[key]=0;
getdis(key);
for(int i=1;i<=n;i++)
{
if(dis[i]>city[key].d)dp[key][i]=inf;
else
{
dp[key][i]=city[i].w;
for(int j=0;j<len;j++)
{
int v=tree[key][j].child;
if(v==parent)continue;
dp[key][i]+=min(best[v],dp[v][i]-city[i].w);
}
best[key]=min(dp[key][i],best[key]);
}
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
init();
dfs(1,0);
printf("%d\n",best[1]);
}
return 0;
}