首页 > 其他 > 详细

【poj2152】【Fire】【树形dp】

时间:2015-04-25 18:22:37      阅读:158      评论:0      收藏:0      [点我收藏+]

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]);
    }
}

【poj2152】【Fire】【树形dp】

原文:http://blog.csdn.net/fzhvampire/article/details/45271581

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!