zoukankan      html  css  js  c++  java
  • 洛谷P3354 河流

    有点权边权的树,选出k个关键点,根必须选。每个点的贡献为点权 * 到最近的关键祖先的距离。求最小总贡献。

    解:树形DP是最毒瘤的算法......

    设fxij表示以x为根的子树中选了j个关键点,且x的最近关键祖先是它的i级祖先时的最小贡献。

    初态:fxi0 = val[x] * dis(),fx01 = 0。

    转移:注意到各个i之间是分层转移的,只有女儿i = 0的情况会转移到母亲的每个i。

    于是先转移fy0r,再分层转移fyjr。每层是一个树上背包。

     1 #include <bits/stdc++.h>
     2 
     3 typedef long long LL;
     4 const int N = 110;
     5 
     6 struct Edge {
     7     int nex, v, len;
     8 }edge[N]; int tp;
     9 
    10 int e[N], n, siz[N], fa[N], val[N], k;
    11 LL f[N][N][N], temp[N][N], d[N];
    12 
    13 inline void add(int x, int y, int z) {
    14     tp++;
    15     edge[tp].v = y;
    16     edge[tp].len = z;
    17     edge[tp].nex = e[x];
    18     e[x] = tp;
    19     return;
    20 }
    21 
    22 inline LL dis(int x, int t) {
    23     int y = x;
    24     for(int i = 1; i <= t; i++) {
    25         y = fa[y];
    26     }
    27     return d[x] - d[y];
    28 }
    29 /*
    30 2 1
    31 1 0 2
    32 1 0 3
    33 ------------- 2
    34 */
    35 void DFS(int x) {
    36     //printf("x = %d d[x] = %lld  fa[x] = %d 
    ", x, d[x], fa[x]);
    37     siz[x] = 1;
    38     for(int i = 1; i <= n; i++) {
    39         f[x][i][0] = val[x] * dis(x, i);
    40         //printf("f %d %d %d  = %lld = %d * %d
    ", x, i, 0, f[x][i][0], val[x], dis(x, i));
    41     }
    42     f[x][0][1] = 0;
    43     for(int i = e[x]; i; i = edge[i].nex) {
    44         int y = edge[i].v;
    45         //printf("%d -> %d 
    ", x, y);
    46         d[y] = d[x] + edge[i].len;
    47         DFS(y);
    48         memcpy(temp, f[x], sizeof(f[x]));
    49         memset(f[x], 0x3f, sizeof(f[x]));
    50         for(int j = 0; j <= n; j++) { /// dis
    51             for(int p = 1; p <= k; p++) {
    52                 for(int r = 1; r <= p; r++) {
    53                     //printf("r = %d -> %lld 
    ", r, temp[j][p - r] + f[y][0][r]);
    54                     f[x][j][p] = std::min(f[x][j][p], temp[j][p - r] + f[y][0][r]); /// don't choose
    55                 }
    56                 //printf("1f %d %d %d =  %lld 
    ", x, j, p, f[x][j][p]);
    57             }
    58         }
    59         for(int j = 0; j <= n; j++) {
    60             for(int p = k; p >= 0; p--) {
    61                 for(int r = 0; r <= p; r++) {
    62                     f[x][j][p] = std::min(f[x][j][p], temp[j][p - r] + f[y][j + 1][r]); /// choose y
    63                 }
    64                 //printf("2f %d %d %d =  %lld 
    ", x, j, p, f[x][j][p]);
    65             }
    66         }
    67         siz[x] += siz[y];
    68     }
    69     //printf("%d return 
    ", x);
    70     return;
    71 }
    72 
    73 int main() {
    74     memset(f, 0x3f, sizeof(f));
    75     scanf("%d%d", &n, &k);
    76     n++; k++;
    77     k = std::min(k, n);
    78     for(int i = 2, x, y; i <= n; i++) {
    79         scanf("%d%d%d", &val[i], &x, &y);
    80         add(x + 1, i, y);
    81         fa[i] = x + 1;
    82     }
    83 
    84     DFS(1);
    85 
    86     printf("%lld
    ", f[1][0][k]);
    87     return 0;
    88 }
    AC代码

    注意每次合并子树的时候,原来的值不能保留。保留下来的要加上fy0r

    恶心死我了...

  • 相关阅读:
    C#的集合类(二)Hashtable、SortedList、Dictionary
    下载文件-JavaScript
    ASP.NET(C#)图片加文字、图片水印
    C# DataTable 互转 List<T>
    C# 将 DataTable 转 List<T>、首行转 T
    C# 金额转中文大写
    监听端口守护进程
    数据库操作的九大步骤
    日志方法
    sqlserver 查找所有子级
  • 原文地址:https://www.cnblogs.com/huyufeifei/p/10517880.html
Copyright © 2011-2022 走看看