poj 2152 Fire(树形dp)

来源:互联网 发布:slmmsk软件下载 编辑:程序博客网 时间:2024/04/27 23:56

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 0x3f3f3f3fusing 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;}
0 0