【poj2152】【Fire】【树形dp】

来源:互联网 发布:手机运动软件 编辑:程序博客网 时间:2024/05/20 09:21

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]);    }}
3 0
原创粉丝点击