Time Limit: 2000MS | Memory Limit: 65536K | |
Total Submissions: 1412 | Accepted: 751 |
Description
Input
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
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
Source
POJ Monthly,Lou Tiancheng
//一知半解并不是什么好习惯。
这是一道树形DP的题目,详解可以在陈启峰的2006国家集训队论文中看到。
这道题花费了我很多时间,无论是陈启峰的论文,解题报告,还是其他人的题解,都有接触过一些。
说实话,难受。而且完全没有之后能做出相同题目的自信。
----------------------------------------以下,正题--------------------------------------------------------------------------
题意大概是讲某个国家最近有人喜欢玩火(笑)所以需要建设消防站的故事,条件在下面会分析而且如果只是来看翻译就想做题的话推荐你直接换题吧就是这样。
首先我们要理解题目给出的条件:
1、每个城市可以建造消防站点,对于城市k来说消耗为w[k]。
2、每个城市可以选择距离自己最近的一个一定距离内的消防站作为负责自己的消防站,对于城市k来说距离不能超过d[k]。
3、每个城市需要有至少需要一个负责的消防站。
因为是树形DP所以转换成有根树。
首先,对于条件2,3,我们可以看作“距离k以内存在一个消防站作为负责站点”就行了。因为其实只要范围内存在一个站点,就可以满足条件。
然后,我们来分析我们设置的状态和状态的转移。
设定状态dp[i][j],表示城市i以j为负责站点的情况下的最优解,用best。那么此时的情况就有三种:
③ 如果j是i的父节点,那么对于i的直接子节点k,k可以选取点j为负责站或者选取自己的子树中的元素作为负责站。
即 dp[i][j]+=min(dp[k][j],best[k])
⑥ 如果点j就是点i,那么对于i的直接子节点k,k同样可以选取点j作为负责站,或者选择子树的元素作为负责站,不过要加上修建站点的花费。
即 dp[i][j]+=w[i]+min(dp[k][j],best[k])
之所以只从那个dp[k][j]转换,是因为负责站点在设定上至少是距离最近的点,因此在非k点子节点的时候距离最近的必然是点j。
⑨ 如果j是i的子节点,那么对于i的子节点k,分为含j的子树和不含j的子树。其中不含j的子树跟第一种相同;对于含j的子树,
则有 dp[i][j]+=dp[k][j]
理由和前面的类似,也是因为最短。
明明打算写的简单易懂但是感觉还是讲不清楚。。。
代码如下
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
const int MAXN=2005;
const int MAXM=1005;
const int INF=0x3f3f3f3f;
int n;
int cost[MAXM];
int D[MAXM];
int e_max;
int u[MAXN],v[MAXN],w[MAXN],nex[MAXN];
int fir[MAXM];
void add_edge(int a,int b,int c)
{
int e=e_max++;
u[e]=a;
v[e]=b;
w[e]=c;
nex[e]=fir[a];
fir[a]=e;
}
int dp[1005][1005];
int dis[1005];
int point[1005];
void DfsDis(int x,int father,int len,int mark)
{
//printf("%d %d %d\n",x,father,len);
dis[x]=len;
point[x]=mark;
for(int e=fir[x];~e;e=nex[e])
{
if(v[e]==father) continue;
DfsDis(v[e],x,len+w[e],mark);
}
}
int best[MAXM];
void Dfs(int x,int father)
{
//printf("++%d %d++\n",x,father);
for(int e=fir[x];~e;e=nex[e])
{
if(v[e]==father) continue;
Dfs(v[e],x);
}
point[x]=x;
dis[x]=0;
for(int e=fir[x];~e;e=nex[e])
DfsDis(v[e],x,w[e],v[e]);
/*
for(int i=1;i<=5;i++) printf("%d %d\n",point[i],dis[i]);
system("pause");
*/
for(int i=1;i<=n;i++)
{
dp[x][i]=0;
if(dis[i]>D[x]) dp[x][i]=INF;
else if(point[i]==father)
{
for(int e=fir[x];~e;e=nex[e])
dp[x][i]+=min(dp[v[e]][i],best[v[e]]);
}
else if(x==i)
{
//cout<<x<<" "<<i<<endl;
dp[x][i]+=cost[x];
for(int e=fir[x];~e;e=nex[e])
dp[x][i]+=min(dp[v[e]][i],best[v[e]]);
}
else
{
for(int e=fir[x];~e;e=nex[e])
{
if(v[e]==point[i])
{
dp[x][i]+=dp[point[i]][i];
continue;
}
dp[x][i]+=min(dp[v[e]][i],best[v[e]]);
}
}
}
best[x]=INF;
for(int i=1;i<=n;i++)
{
if(point[i]!=father)
{
best[x]=min(dp[x][i],best[x]);
}
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
memset(dp,0,sizeof dp);
memset(fir,-1,sizeof fir);
e_max=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&cost[i]);
}
for(int i=1;i<=n;i++)
{
scanf("%d",&D[i]);
}
for(int i=0;i<n-1;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add_edge(u,v,w);
add_edge(v,u,w);
}
Dfs(1,0);
// for(int i=1;i<=n;i++)
// {
// for(int j=1;j<=n;j++)
// printf("%d ",dp[i][j]);
// printf("\n");
// }
printf("%d\n",best[1]);
}
return 0;
}