【poj2152】【Fire】【树形dp】

Fire

Time Limit: 2000MS Memory Limit: 65536K

Total Submissions: 1161 Accepted: 595

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

题意:给定n个节点组成的树,树有边权.现在要在一些点上建立消防站,每个点建站都有个cost[i],如果不在当前的点上建站,也要依赖其他的消防站,并且距离不超过limit[i]。求符合上述条件的最小费用建站方案。n <= 1000.

这道题是一道比较神的树形dp,我是在看了陈启峰2006年的国家集训队论文才会的:《一张一弛,解题之道 ——“约制、放宽”方法在解题中的应用》

既然已经有论文了,我就简单的说一下吧:

作为正常的思路,我们维护best[i]表示节点在节点i时的最小花费。

但是我们发现这道题有一个比较麻(dan)烦(teng)的一点就是他还有一个限制。这样我们所想出的动规就比较难解决了。

那么我们可以想到这个限制其实就是要找到离节点i最近的消防站就可以了。

但是就是这样,我们还是比较难以写出这个转移方程来。。。

我们再进一步往后想,其实我们没有必要找到最近的那个节点,只需要知道在i的限制范围之内的消防站就好了。——这一步就是论文里所说的放宽要求。

首先我们先要知道一个性质:假设在p1—>pm这个序列上的所有城市的负责站都为pi节点,那么总有一个最优解满足上述的性质。

至于证明我就不在这里赘述了,大家去看论文吧。

假设我们已经得到了这个性质,纳闷我们可以保存这样一个数组

f[i][j]表示在节点i的时候选择j点作为节点i的负责站的最优值。

那么我们就可以将转移方程分为这么几个阶段:

①:当dis[i][j]>limit[i]时我们不需要去管他。

②:当dis[i][j]<=limit[i]时还可以分成这么几个阶段:

(1):当j不是i的子树的时候,那么i的子节点x就有两种选择,一个是选择x的子树中的消防站,那么f[i][j]=best[x]。另 一种就是选择在x外的节点作为负责站,呢们根据上面个的那个性质我们可以知道i的负责站跟x的一样,那么f[i][j]=f[x][j]。

f[i][j]+=min(f[x][j],best[x]);

(2):选择i节点建消防站。

f[i][i]+=cost[i]+min(best[x],f[x][i]);

(3):选择i节点的子树为负责站,其实这种情况跟第一种很像

f[i][j]+=min(f[k][i]-cost[j],best[k])+cost[j];

这样以后我们就可以求解了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int N=1010;
const int inf=210000000;
struct S{
    int v,len;
}point;
vector <S> tr[N];
int t,n,best[N],dis[N][N],f[N][N],cost[N],limit[N],now;
/*------  预处理两点之间的距离  --------*/
void dfs(int x,int last,int Len)
{
    int i,j,v,len;
    dis[now][x]=Len;
    for(i=0;i<tr[x].size();++i){
        v=tr[x][i].v;
        len=tr[x][i].len;
        if(v!=last) dfs(v,x,Len+len);
    }
}
void dp(int x,int last)
{
    int i,j,v,len;
    for(i=0;i<tr[x].size();++i)
      if(tr[x][i].v!=last)
        dp(tr[x][i].v,x);
    for(i=1;i<=n;++i){
        if(dis[x][i]<=limit[x]){
            f[x][i]=cost[i];
            for(j=0;j<tr[x].size();++j){
                v=tr[x][j].v;
                len=tr[x][j].len;
                if(v!=last) f[x][i]+=min(f[v][i]-cost[i],best[v]);  //第(2)、(3)种
              }
            best[x]=min(best[x],f[x][i]);  //第(1)种情况
        }
    }
}
int main()
{
    scanf("%d",&t);
    while(t--){
        int i,j,x,y,z;
        scanf("%d",&n);
        for(i=1;i<=n;++i) scanf("%d",&cost[i]);
        for(i=1;i<=n;++i) scanf("%d",&limit[i]);
        for(i=1;i<=n;++i){
            tr[i].clear();
            best[i]=inf;
        }
        for(i=1;i<=n;++i)
          for(j=1;j<=n;++j)
            f[i][j]=inf;
        for(i=1;i<n;++i){
            scanf("%d%d%d",&x,&y,&z);
            point.v=y;point.len=z;
            tr[x].push_back(point);
            point.v=x;point.len=z;
            tr[y].push_back(point);
        }
        for(i=1;i<=n;++i){
            now=i;
            dfs(i,0,0);
        }
        dp(1,0);
        printf("%d\n",best[1]);
    }
}
时间: 2024-08-08 05:21:52

【poj2152】【Fire】【树形dp】的相关文章

poj2152(Fire) 树形DP

题目链接:http://poj.org/problem?id=2152 题意:一棵带边权的树,边权表示节点间距离,在i上建立消防站的代价是w[i],如果在一点i没建消防站,那么它与距离这个点最近的消防站之间的距离不能大于d[i].问满足建站最小的花费: 解法:看了陈启峰的论文才会的,感觉挺难的,不过论文里分情况讨论了,应该不需要:dp[i][j]表示在i处选择j处作为供应站(但是并不一定要求是最近的,这样即使有更近的也无所谓,和论文里有出入.),best[i]表示i及其子树满足要求的最小花费,那

POJ 2152 Fire(树形DP)

题意: 思路:令F[i][j]表示 的最小费用.Best[i]表示以i为根节点的子树多有节点都找到负责消防站的最小费用. 好难的题... 1 #include<algorithm> 2 #include<cstdio> 3 #include<cmath> 4 #include<cstring> 5 #include<iostream> 6 int tot,go[200005],first[200005],next[200005],val[2000

Fire (poj 2152 树形dp)

Fire (poj 2152 树形dp) 给定一棵n个结点的树(1<n<=1000).现在要选择某些点,使得整棵树都被覆盖到.当选择第i个点的时候,可以覆盖和它距离在d[i]之内的结点,同时花费为v[i].问最小花费. 以前做过一道类似的题(水库),这道题也差不多.首先来考虑,用\(best[i]\)表示以i为根的子树的最小花费.这样做有什么问题呢?它无法很好的处理消防站重复建的问题. 所以换一种做法.\(best[i]\)依然表示原来的含义,新建一个数组\(f[i][j]\),表示当i这个结

POJ 2152 Fire (树形DP,经典)

题意:给定一棵n个节点的树,要在某些点上建设消防站,使得所有点都能够通过某个消防站解决消防问题,但是每个点的建站费用不同,能够保证该点安全的消防站的距离上限也不同.给定每个点的建站费用以及最远的消防站距离上限,求保证该树安全的最小花费. 思路: 要选择部分点来建站解决消防问题,而总花费是有最优解的. 如何进行树形DP? 假设某点t的所有子树的消防问题都解决,而且已经获得最优解了,那么现在考虑的是点t的最优解问题,点t可以依靠任何点只要不超过距离限制即可,那枚举一下所有点试试,一旦t依靠某个点j解

树形 DP 总结

本文转自:http://blog.csdn.net/angon823/article/details/52334548 介绍 1.什么是树型动态规划 顾名思义,树型动态规划就是在"树"的数据结构上的动态规划,平时作的动态规划都是线性的或者是建立在图上的,线性的动态规划有二种方向既向前和向后,相应的线性的动态规划有二种方法既顺推与逆推,而树型动态规划是建立在树上的,所以也相应的有二个方向: 1.叶->根:在回溯的时候从叶子节点往上更新信息 2.根 - >叶:往往是在从叶往根d

【转】【DP_树形DP专辑】【9月9最新更新】【from zeroclock&#39;s blog】

树,一种十分优美的数据结构,因为它本身就具有的递归性,所以它和子树见能相互传递很多信息,还因为它作为被限制的图在上面可进行的操作更多,所以各种用于不同地方的树都出现了,二叉树.三叉树.静态搜索树.AVL树,线段树.SPLAY树,后缀树等等.. 枚举那么多种数据结构只是想说树方面的内容相当多,本专辑只针对在树上的动态规划,即树形DP.做树形DP一般步骤是先将树转换为有根树,然后在树上进行深搜操作,从子节点或子树中返回信息层层往上更新至根节点.这里面的关键就是返回的信息部分,这个也没一般性的东西可讲

HDU-2196 Computer (树形DP)

最近在看树形DP,这题应该是树形DP的经典题了,写完以后还是有点感觉的.之后看了discuss可以用树分治来做,以后再试一试. 题目大意 找到带权树上离每个点的最远点.︿( ̄︶ ̄)︿ 题解: 对于每一个点的最远点,就是以这个点为根到所有叶子节点的最长距离.但是如果确定根的话,除了根节点外,只能找到每个节点(度数-1)个子树的最大值,剩下一个子树是该节点当前的父亲节点. 所以当前节点的最远点在当前节点子树的所有叶子节点以及父亲节点的最远点上(当父亲节点的最远点不在当前节点的子树上时), 如果父亲节

UVA-01220 Party at Hali-Bula (树形DP+map)

题目链接:https://vjudge.net/problem/UVA-1220 思路: 树形DP模板题,求最大人数很简单,难点在于如何判断最大人数的名单是否有不同的情况: 解决方法是用一个数组f[manx][2]记录该节点是否出场的情况,为真时代表有多种情况; 具体讨论: 当父节点的值加上某个子节点的值时,他的f的情况也和该子节点一样: 当某个节点dp(i, 0) == dp(i, 1), 则该节点以及它的父节点也一定有多种情况(父节点必定取其中之一). Code: 1 #include<bi

HDU 1520 树形dp裸题

1.HDU 1520  Anniversary party 2.总结:第一道树形dp,有点纠结 题意:公司聚会,员工与直接上司不能同时来,求最大权值和 #include<iostream> #include<cstring> #include<cmath> #include<queue> #include<algorithm> #include<cstdio> #define max(a,b) a>b?a:b using nam