题意:有N个城市,连接这些城市的道路组成了一个树形的结构。现在想在一些城市建立消防站。如果选择在城市i建立消防站,需要的费用是
w i
,这个消防站负责该城市的防火工作。如果不选择建立消防站,那在距离为
d i
的范围内要有一个消防站来负责城市i的防火工作。现在想让所有城市都能保证安全,求最小的费用。
思路:想了很长时间,以为和前面的独立集是相关的,但是怎么也想不出来。。就去搜题解了。
在陈启峰的论文中《一张一弛_解题之道》有这道题的解法。
因为要利用任意两点间的距离,我们可以用
Θ(n 2 )
的方法得到这些距离。
设dp[u][i]表示以u为根的子树的每个节点都有消防站负责,且节点u的负责消防站是建立在节点i上的最小花费。
best[u]表示以u为根的子树的每个节点以u内的某个节点作为负责站的最小花费。
容易得到:
best[u]=min(dp[u][i]),i∈subtree(u)
下面考虑dp[u][i]的状态转移:
1.如果 dist(u,i)>d u ,这个时候,节点u是不能以i处的消防站为负责站,此时 dp[u][i]=+∞
2.如果 dist(u,i)<=d u ,这个时候,节点u就能选择以i处的消防站为负责站了。对于u的儿子v,可以选择节点i作为自己的负责站,也可以用自己子树内的消防站。
dp[u][i]=∑ v∈son(u) min(dp[v][i]−w i ,best[v])
初始化条件:
dp[u][i]=c i ,dist(u,i)≤d u
这样在,最后的答案就是best[1]。
注意:
1.对于best[u]的求解,我们直接在DFS的过程中求就可以了,不用刻意的标记以u为根的子树的节点。因为DFS的调用过程就反应了节点的从属关系。
代码如下:
#include <iostream>
#include <cstring>
#include <map>
#include <string>
#include <algorithm>
using namespace std;
const int MAX = 1010;
struct edge{
int to,w;
edge(){}
edge(int t, int ww):to(t),w(ww){}
} edges[MAX<<1];
int N;
int nxt[MAX<<1],head[MAX],tot;
int dis[MAX][MAX],cost[MAX],lim[MAX];
int best[MAX],dp[MAX][MAX];
void init()
{
memset(head,-1,sizeof(head));
memset(best,0x3f,sizeof(best));
memset(dp,0x3f,sizeof(dp));
tot = 0;
}
void addedge(int u, int v, int w)
{
edges[tot] = edge(v,w);
nxt[tot] = head[u];
head[u] = tot++;
}
void dfs(int u, int p, int di,int root)
{
dis[root][u] = di;
for(int i = head[u]; ~i; i = nxt[i]){
edge & e = edges[i];
if(e.to == p) continue;
dfs(e.to,u,di+e.w,root);
}
}
void solve(int u, int p)
{
for(int i = 1; i <= N; ++i)
if(dis[u][i] <= lim[u])
dp[u][i] = cost[i];
for(int i = head[u]; ~i; i = nxt[i]){
edge & e = edges[i];
if(e.to == p) continue;
solve(e.to,u);
for(int j = 1; j <= N; ++j)
if(dis[u][j] <= lim[u])
dp[u][j] += min(dp[e.to][j] - cost[j],best[e.to]);
}
for(int i = 1; i <= N; ++i)
best[u] = min(best[u],dp[u][i]);
}
int main(void)
{
//freopen("input.txt","r",stdin);
int T;
scanf("%d",&T);
while(T--){
init();
scanf("%d",&N);
for(int i = 1; i <= N; ++i)
scanf("%d",&cost[i]);
for(int i = 1; i <= N; ++i)
scanf("%d",&lim[i]);
for(int i = 1; i <= N-1; ++i){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
addedge(u,v,w);
addedge(v,u,w);
}
for(int i = 1; i <= N; ++i)
dfs(i,0,0,i);
solve(1,0);
printf("%d\n",best[1]);
}
return 0;
}