陈启峰的论文题
这个树形dp还是比较难的,没有想到。
dp[i][j]表示以i为根的子树,i结点选j为负责站,所花的最少代价。
best[i]=min{dp[i][j] | 1<=j<=n}
当dis[i][j]>limit[i],dp[i][j]=∞
当dis[i][j]<=limit[i],dp[i][j]=cost[j]+sigma{min(best[child[i]],dp[child[i]][j]-cost[j])}
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 1010
#define inf 0x3f3f3f3f
using namespace std;
int n,cost[N],limit[N],tim[N],dep,son[N];
int dis[N][N];
int dp[N][N],best[N];
int head[N],cnt;
struct Edge{
int v,w,next;
}edge[N*2];
void init(){
memset(head,-1,sizeof(head));
memset(tim,0,sizeof(tim));
cnt=dep=0;
}
void addedge(int u,int v,int w){
edge[cnt].v=v;
edge[cnt].w=w;
edge[cnt].next=head[u];
head[u]=cnt++;
edge[cnt].v=u;
edge[cnt].w=w;
edge[cnt].next=head[v];
head[v]=cnt++;
}
void init_dfs(int u,int fa){
tim[u]=++dep;
son[u]=1;
for(int i=head[u];i!=-1;i=edge[i].next){
int v=edge[i].v;
if(v==fa)continue;
init_dfs(v,u);
son[u]+=son[v];
}
}
void cal_dis(int u,int fa,int now){
for(int i=head[u];i!=-1;i=edge[i].next){
int v=edge[i].v;
if(v==fa)continue;
dis[now][v]=dis[now][u]+edge[i].w;
cal_dis(v,u,now);
}
}
void dfs(int u,int fa){
for(int i=head[u];i!=-1;i=edge[i].next){
int v=edge[i].v;
if(v==fa) continue;
dfs(v,u);
}
best[u]=inf;
for(int j=0;j<n;j++){
if(dis[u][j]<=limit[u]){
dp[u][j]=cost[j];
for(int i=head[u];i!=-1;i=edge[i].next){
int v=edge[i].v;
if(v==fa) continue;
dp[u][j]+=min(best[v],dp[v][j]-cost[j]); //当u选择j时,v不会选择j的父亲及往上更远的地方(因为选j是可以,而且更省,不用花那个更远节点的cost),
//而v如果选择其子树之外的其他节点那一定不是最优的情况,通过取min操作会把它排除
}
if(tim[j]>=tim[u] && tim[j]<=tim[u]+son[u]-1)
best[u]=min(best[u],dp[u][j]);
}
else dp[u][j]=inf;
}
}
int main(){
int T;
scanf("%d",&T);
for(int t=0;t<T;t++){
init();
scanf("%d",&n);
for(int i=0;i<n;i++) scanf("%d",&cost[i]);
for(int i=0;i<n;i++) scanf("%d",&limit[i]);
for(int i=0;i<n-1;i++){
int u,v,w;
scanf("%d %d %d",&u,&v,&w);
u--;v--;
addedge(u,v,w);
}
init_dfs(0,-1);
for(int i=0;i<n;i++){
dis[i][i]=0;
cal_dis(i,-1,i);
}
dfs(0,-1);
printf("%d\n",best[0]);
}
}