题目大意:
Z国有n个城市,从1到n给这些城市编号。城市之间连着高速公路,并且每两个城市之间有且只有一条通路。不同的高速公路可能有不同的长度。
最近Z国经常发生火灾,所以当地政府决定在某些城市修建一些消防站。在城市k修建一个消防站须要花费大小为A的费用。函数A对于不同的城市可能有不同的取值。
如果在城市k没有消防站,那么它到离它最近的消防站的距离不能超过B。每个城市在不超过距离B的前提下,必须选择最近的消防站作为负责站。函数B对于不同的城市可能有不同的取值。
为了节省钱,当地政府希望你用最少的总费用修建一些消防站,并且使得这些消防站满足上述的要求。
—–by lyd学长的PPT
分析:
f[i]代表以i为根的子树所需最小费用
D[i][j]代表i被j覆盖所需要的最小费用
dis[i][j]代表ij的距离
首先,我们要满足dis[i][j]<=d[i]才可以保证i被j覆盖
所以
if(dis[i][j]>d[i])
D[i][j]=inf
然后我们枚举j来更新root
D[i,j]=cost[j]+∑Min{ D[to[i],j]-cost[j] , f[to[i]] },dis[i,j]<=d[i]
如果to[i]没有被j覆盖,那么直接取f[to[i]]更新即可
如果to[i]]被j覆盖,我们就要减去cost[j](因为你已经建过一遍j了,但是你如果想再建一遍我也不介意>_< >o<)
f[root]=min(D[i][j]);
代码如下:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn=1000+5;
int cas,n,cnt,cost[maxn],d[maxn],hd[maxn],to[maxn*2],nxt[maxn*2],w[maxn*2],f[maxn],D[maxn][maxn],dis[maxn][maxn];
void add(int x,int y,int z){
to[cnt]=y;
w[cnt]=z;
nxt[cnt]=hd[x];
hd[x]=cnt++;
dis[x][y]=dis[y][x]=z;
}
void calc(int root,int fa,int now){
for(int i=hd[root];i!=-1;i=nxt[i]){
if(to[i]==fa)
continue;
dis[now][to[i]]=dis[now][root]+w[i];
calc(to[i],root,now);
}
}
void dfs(int root,int fa){
int ans=inf;
for(int i=hd[root];i!=-1;i=nxt[i])
if(to[i]!=fa)
dfs(to[i],root);
for(int j=1;j<=n;j++){
if(dis[root][j]>d[root]){
D[root][j]=inf;
continue;
}
D[root][j]=cost[j];
int temp=0;
for(int i=hd[root];i!=-1;i=nxt[i]){
if(to[i]==fa)
continue;
D[root][j]+=min(D[to[i]][j]-cost[j],f[to[i]]);
}
f[root]=min(D[root][j],f[root]);
}
}
signed main(void){
scanf("%d",&cas);
while(cas--){
cnt=0;
scanf("%d",&n);
memset(f,inf,sizeof(f));
memset(hd,-1,sizeof(hd));
memset(dis,0,sizeof(dis));
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=1,a,b,c;i<n;i++)
scanf("%d%d%d",&a,&b,&c),add(a,b,c),add(b,a,c);
for(int i=1;i<=n;i++)
dis[i][i]=0,calc(i,-1,i);
dfs(1,-1);
cout<<f[1]<<endl;
}
return 0;
}
by >o< neighthorn