POJ2152 Fire
http://poj.org/problem?id=2152
树型DP
数据范围较小,首先,暴力求出两点间距离
令\(f[i]\)表示以\(i\)为根的子树全部被消防站覆盖的最小花费,\(g[i][j]\)表示以\(i\)为根的子树全部被消防站覆盖,且\(u\)使用\(i\)的消防站的最小花费
\[f[u]=\min_{1\le i \le n} g[u][i]\\
g[u][i]=w[i]+\min_{v是u的儿子} (g[v][i]-w[i],f[v])\\
g[v][i]-w[i]:v也从属于i,那么i站被建了两次,需要减去重复部分\\
\min_{v是u的儿子} (g[v][i]-w[i],f[v])保证了u的子节点被完全覆盖,\\
同时u也被覆盖了,从而保证了以u为根的子树全部被消防站覆盖
\]
\(C++ Code:\)
#include<cstdio>
#include<iostream>
#include<cstring>
#define INF 1000000009
#define N 2005
#define RN 1005
using namespace std;
int T,x,y,z,n,rt,tot,head[RN],nxt[N],d1[N],d2[N],f[RN],g[RN][RN],dis[RN][RN],w[RN],d[RN];
void Clear()
{
for (int i=0;i<=RN;f[i]=INF,i++)
for (int j=0;j<=RN;j++)
g[i][j]=INF;
memset(head,0,sizeof head);
tot=0;
}
void add(int x,int y,int z)
{
tot++;
d1[tot]=y,d2[tot]=z;
nxt[tot]=head[x],head[x]=tot;
}
void dfs(int u,int f)
{
for (int i=head[u];i;i=nxt[i])
{
int v=d1[i];
int cost=d2[i];
if (v==f)
continue;
dis[rt][v]=dis[rt][u]+cost;
dfs(v,u);
}
}
void tree_dp(int u,int F)
{
for (int i=1;i<=n;i++)
if (dis[u][i]<=d[u])
g[u][i]=w[i]; else
g[u][i]=INF;
for (int i=head[u];i;i=nxt[i])
{
int v=d1[i];
if (v==F)
continue;
tree_dp(v,u);
}
for (int i=head[u];i;i=nxt[i])
{
int v=d1[i];
if (v==F)
continue;
for (int i=1;i<=n;i++)
if (dis[u][i]<=d[u])
g[u][i]+=min(f[v],g[v][i]-w[i]);
}
for (int i=1;i<=n;i++)
f[u]=min(f[u],g[u][i]);
}
int main()
{
scanf("%d",&T);
while (T --> 0)
{
Clear();
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%d",&w[i]);
for (int i=1;i<=n;i++)
scanf("%d",&d[i]);
for (int i=1;i<n;i++)
{
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
add(y,x,z);
}
for (int i=1;i<=n;i++)
{
dis[i][i]=0;
rt=i;
dfs(i,0);
}
tree_dp(1,0);
cout << f[1] << endl;
}
}