poj 2152 Fire(树形dp)

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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值