河流(2005 IOI)

Posted Milky Way

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了河流(2005 IOI)相关的知识,希望对你有一定的参考价值。

题目

  第一步 复杂度分析

    由于 n 的级别是100的,所以可以往O(n^3)上面想,数组可以开到三维。

  第二步 类比

    类比普通的树龟,我们通常设F[i][j]表示在以 i 为根的子树上选取 j 个结点的最优解。但这道题并不同于普通的树龟。所以需要寻找它与普通树龟的根本差别

    可以发现这道题多了一个DP影响因素:结点 i 上方的伐木场。由于 i 上方的伐木场是不确定的,因此设F[i][j]有后效性。

  第三步 化繁为简

    既然 i 上方的伐木场会影响DP的值,我们就把它加入维度,该结点称为‘相对祖先’,是从 i 向上走第一个遇到的有伐木场的结点。

    设F[i][j][k]表示在以 i 为根的子树上(其相对祖先为结点 j)选取 k 个结点建设伐木场的最小花费(这个最小花费统计到相对祖先为止)。

    而结点 i 上是否建有伐木场会影响 i 到 j 这段距离是否加入计算,于是把 F 一分为二,用 f[i][j][k] 表示 i 处无伐木场,g[i][j][k] 表示 i 处建有伐木场。

    这样,就基本转化成了普通树龟的题型。

  第四步 分类讨论列方程

    f: 若不在 i 处建伐木场, 则 f[i][j][k] 为 i 的 k 个儿子到 j(儿子以 j 为相对祖先)的费用, 加上 i 到 j 的费用;

    g: 若在 i 处建伐木场, 则 g[i][j][k] 为 i 的 k 个儿子到 i (儿子以 i 为相对祖先)的费用;

    F = min(f, g).

 

  第五步 敲代码

多叉树做法

技术分享图片
 1 #include<cstdio>
 2 #include<string> 
 3 
 4 const int N = 100 + 10;
 5 int n, m, w[N], d[N], head[N], next[N], sum[N], sta[N], top, f[N][N][60], g[N][N][60];
 6 
 7 int min(int x, int y) {
 8     if (x <= y) return x;
 9     return y;
10 }
11 
12 int read() {
13     int x = 0, f = 1;
14     char c = getchar();
15     while (!isdigit(c)) {
16         if (c == -) f = -1;
17         c = getchar();
18     }
19     while (isdigit(c)) {
20         x = (x << 3) + (x << 1) + (c ^ 48);
21         c = getchar();
22     }
23     return x * f;
24 }
25 
26 void DP(int u) {
27     sta[++top] = u;
28     for (int i = head[u]; i; i = next[i]) {
29         sum[i] = sum[u] + d[i];
30         DP(i);
31         for (int a = 1; a <= top; ++ a) // ancient 
32             for (int j = m; j >= 0; -- j) {
33                 f[u][sta[a]][j] += f[i][sta[a]][0];
34                 g[u][sta[a]][j] += f[i][u][0];
35                 for (int k = 0; k <= j; ++ k) {
36                     f[u][sta[a]][j] = min(f[u][sta[a]][j], f[i][sta[a]][k] + f[u][sta[a]][j - k]);
37                     g[u][sta[a]][j] = min(g[u][sta[a]][j], f[i][u][k] + g[u][sta[a]][j - k]);
38                 }
39             }
40     }
41     for (int a = 1; a <= top; ++ a)
42         for (int j = 0; j <= m; ++ j)
43             if (j == 0)
44                 f[u][sta[a]][j] += w[u] * (sum[u] - sum[sta[a]]);
45             else
46                 f[u][sta[a]][j] = min(f[u][sta[a]][j] + w[u] * (sum[u] - sum[sta[a]]), g[u][sta[a]][j - 1]);
47     sta[top--] = 0;
48 }
49 
50 int main() {
51     n = read(), m = read();
52     for (int i = 1, v; i <= n; ++ i) {
53         w[i] = read(), v = read(), d[i] = read();
54         next[i] = head[v], head[v] = i;
55     }
56     DP(0);
57     printf("%d\n", f[0][0][m]);
58     return 0;
59 }
River

 

 

转二叉树做法

 


以上是关于河流(2005 IOI)的主要内容,如果未能解决你的问题,请参考以下文章

IOI 2005/bzoj 1812:riv 河流

河流(2005 IOI)

bzoj1812 [IOI2005]riv河流

bzoj1812 [Ioi2005]riv

1812: [Ioi2005]riv

BZOJ1812: [Ioi2005]riv