题意:
给你一颗树,边上权值表示距离,一个点上可以建消防站,花费为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;
}