HLG 1329 Leyni, 罗莉 与 游乐场【树形DP】
题意: 给一个树形的图,可以在任意一个位置建造游乐场,每个游乐场都有相应的造价,如果该节点建有游乐场,那么改点的人到该游乐场就不需要花费,
否则需要花费所在位置到游乐场的路径长度对应的费用,问怎么建游乐场能使得总花费最小。
分析: 树形DP。
用 f[i][j] 表示以 i 为根节点的子树以 j 为游乐场的最小花费,为了便于状态转移,这个游乐
场先不计费,f[i][j] 中的最优的 j 一定是其某个子节点,这样就长生了最优子结构,所以计算
f[i][j]时 ,f[i][j] 的值可以由其子节点来确定,状态转移方程如下:
f[i][j]=d[len[i][j]]+sum( min( f[c][j],f[c][opt[c]]+k) )
其中 c 是 i 的孩子。即考察每个子树的根以 j 为游乐场(这样能够省下 K 元),要么就是不 省
k 元而使用opt[c] 为游乐场,由于子树外的点一定不会是该子树的最优解, 所以 I 的各个子树的
opt[c]一定不同,这里直接加 K 。
f[根][opt[根]]+K就是答案。
code :
View Code
#include<stdio.h> #include<string.h> #define INF 0x1f1f1f1f #define min(a,b)(a)<(b)?(a):(b) int n,p; int g[202][202]; int d[202]; int v[202]; int opt[202]; int f[202][202]; int tot=0; void dfs(int r) { v[r]=1; int i,j,tmp,sum,flag=0; for(i=1;i<=n;i++) if(g[r][i]==1&&!v[i]) { flag=1; dfs(i); } if(flag==0) // 如果是叶子节点 { for(i=1;i<=n;i++) f[r][i]=d[g[r][i]]; // 如果游乐场选在树外的话,f[r][i]为这个叶子节点到i 的距离 opt[r]=r; // 应为当选择了游乐场之后该游乐场不计费,所以当 return; // 当树中只存在一个节点的时候造价为f[i][opt[i]]+k } // 其中 opt[i]=i 即f[i][opt[i]]=0 tmp=INF; for(i=1;i<=n;i++) // 如果不是叶子节点 { f[r][i]=d[g[r][i]]; // 费用要先包含根到游乐场的路程的费用 for(j=1;j<=n;j++) if(g[r][j]==1&&opt[j])// 如果j 是r 的直接后代,并且opt[j]已有值,即J已经选择好了 f[r][i]+=min(f[j][i],f[j][opt[j]]+p);// 游乐场,也说明 j 不是 r 的祖先 if(f[r][i]<tmp) { // 找出建造费用最少的做为opt[r] opt[r]=i; tmp=f[r][i]; } } } int main() { // freopen("D:ce.txt","r",stdin); int i,j,k,t,a,b; scanf("%d",&t); while(t--) { scanf("%d%d",&n,&p); d[0]=0; for(i=1;i<n;i++) scanf("%d",&d[i]); memset(v,0,sizeof(v)); memset(g,0,sizeof(g)); memset(opt,0,sizeof(opt)); memset(f,0,sizeof(f)); for(i=1;i<n;i++) { scanf("%d%d",&a,&b); g[a][b]=g[b][a]=1; } for(k=1;k<=n;k++) // Floyed求任意两点间的最短距离 for(i=1;i<=n;i++) if(g[i][k]) for(j=1;j<=n;j++) { if(j==i)continue; if(g[k][j]&&(g[i][k]+g[k][j]<g[i][j]||g[i][j]==0)) g[i][j]=g[i][k]+g[k][j]; } dfs(1); printf("%d\n",f[1][opt[1]]+p); } return 0; }