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]);
}
}
原文:http://blog.csdn.net/fzhvampire/article/details/45271581