Description
Country Z has N cities, which are numbered from 1 to N. Cities are connected by highways, and there is exact one path between two different cities. Recently country Z often caught fire, so the government decided to build some firehouses in some cities. Build a firehouse in city K cost W(K). W for different cities may be different. If there is not firehouse in city K, the distance between it and the nearest city which has a firehouse, can’t be more than D(K). D for different cities also may be different. To save money, the government wants you to calculate the minimum cost to build firehouses.
Input
The first line of input contains a single integer T representing the number of test cases. The following T blocks each represents a test case.
The first line of each block contains an integer N (1 < N <= 1000). The second line contains N numbers separated by one or more blanks. The I-th number means W(I) (0 < W(I) <= 10000). The third line contains N numbers separated by one or more blanks. The I-th number means D(I) (0 <= D(I) <= 10000). The following N-1 lines each contains three integers u, v, L (1 <= u, v <= N,0 < L <= 1000), which means there is a highway between city u and v of length L.
Output
For each test case output the minimum cost on a single line.
Sample Input
5
5
1 1 1 1 1
1 1 1 1 1
1 2 1
2 3 1
3 4 1
4 5 1
5
1 1 1 1 1
2 1 1 1 2
1 2 1
2 3 1
3 4 1
4 5 1
5
1 1 3 1 1
2 1 1 1 2
1 2 1
2 3 1
3 4 1
4 5 1
4
2 1 1 1
3 4 3 2
1 2 3
1 3 3
1 4 2
4
4 1 1 1
3 4 3 2
1 2 3
1 3 3
1 4 2
Sample Output
2
1
2
2
3
啊……这道题目,刚开始的时候想简单了,最后递推式写着写着就乱了根本推不出来,后来百度了一下发现自己思路差的还是有点远……通过看论文及别人的题解,最后还是和学长讨论了一下才理解的。
关于这道题,在陈启峰2006年的《一张一弛,解题之道 ——“约制、放宽”方法在解题中的应用》论文中以例题出现,有详细的讲解。其中关于dp[i][j]的状态,以及best数组的应用很值得借鉴。(我现在有点迟疑……)
啊!!看题解发现,题解中对于best数组的定义和论文中并不一样。。。
晕……这两种其实真的算是不同的思路了……
其中dp[i][j]表示的状态是:i节点依赖于j节点(j节点必有一个消防站),同时以i为根节点的子树中所有节点都已有相应的消防站并达到最优解。
best[i]则表示以i为根节点,所有子树都有可依赖的消防站,并使费用达到最小(注意:和论文中提到不同的……i的子树中这些节点所依赖的节点不一定仍在i的子树中)
同时我们以dis数组辅助,对于每一次的节点k都进行一次更新,dis[i]代表节点i到k的距离。
这个神奇的状态转移方程……你要是像我一样看着论文来理解你就输了,因为这两者对best的定义都不一样。。。
dp[now][j]=w[j]+sum(min(dp[i][j]-w[j],best[i])) // i为j的孩子。
best[now]=min(dp[now][j],best[now]) //遍历j进行best[now]的更新。
dp[now][j]首先被赋一个初值w[j],因为j节点一定会修建,之后遍历now的子节点i,对于每一个节点i,要取到其最优值,讲道理应该是best[i](我们对best[i]的定义,并且best[i]是从子节点一层层更新上来的,所以可以保证最优),但是因为dp[now][j]一定会在j处修建一个消防站,而dp[i][j]的含义是i依赖于j,其他点达到最优,虽然dp[i][j]>=best[i],但是由于我们此时j一定会修建,所以可以不考虑j处修建的费用,我们此时便可取min(dp[i][j]-w[j],best[i])。
我的天……我都不知道怎么说了……要不是学长的提醒,一直没看出来论文和题解中状态的不同,这也是我困惑了这么长时间的原因……哎……突然又觉得做这道题似乎不去看论文的思路比较好。。。
以下是AC代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>
#include <algorithm>
#define maxn 1010
#define inf 0x3f3f3f3f
using namespace std;
struct node{
int to,len;
node(){};
node(int _to,int _len):to(_to),len(_len) {};
};
vector<node> g[maxn];
int dp[maxn][maxn],dis[maxn],d[maxn],w[maxn],best[maxn];
int t,n;
void getdis(int now,int fa,int dist)
{
dis[now]=dist;
for(int i=0;i<g[now].size();i++)
{
int v=g[now][i].to;
if(v==fa) continue;
int len=g[now][i].len;
getdis(v,now,len+dist);
}
}
void dfs(int now,int fa)
{
for(int i=0;i<g[now].size();i++)
{
int v=g[now][i].to;
if(v==fa) continue;
dfs(v,now);
}
getdis(now,0,0);
for(int i=1;i<=n;i++)
{
if(dis[i]>d[now]) dp[now][i]=inf;
else{
dp[now][i]=w[i];
for(int j=0;j<g[now].size();j++)
{
int v=g[now][j].to;
if(v==fa) continue;
dp[now][i]+=min(best[v],dp[v][i]-w[i]);
}
best[now]=min(best[now],dp[now][i]);
}
}
}
int main()
{
cin>>t;
while(t--)
{
cin>>n;
for(int i=0;i<=n;i++)
g[i].clear();
memset(best,0x3f,sizeof(best));
for(int i=1;i<=n;i++)
cin>>w[i];
for(int i=1;i<=n;i++)
cin>>d[i];
for(int i=1;i<=n-1;i++)
{
int a,b,v;
cin>>a>>b>>v;
g[a].push_back(node(b,v));
g[b].push_back(node(a,v));
}
dfs(1,0);
cout<<best[1]<<endl;
}
return 0;
}