「 Luogu P2230 」X 「 Vijos 1142 」 HXOS系统

题目描述可能稍有偏差,但实质上是一样的。

看下面

题目大意

题面这么长,先说说题意吧。

就是有一个操作系统,他的存储方式是树形的。其中分为文件和目录(文件夹)每一个子目录下只能存储 $K$ 个文件或目录。并且有 $K$ 个指针分别指向这 $K$ 个文件或文件夹。每一个指针都有一个访问时间 $P$。

然后每一个文件或目录都有一个访问时间,文件和目录的访问时间计算方式不相同

  • 文件:访问其所有上级目录(就是这个文件访问路径上的文件夹)的访问时间加上这个文件的指针的访问时间。
  • 文件夹:它的各级子目录下文件的个数 $ imes $ 它的指针的访问时间。通俗一点就是它的子树上的文件的个数为 $x$,它的指针访问时间是 $y$ 那么它的访问时间就是 $x imes y$。这里要说一下题面描述出了锅,最起码我现在看是这样的。

现在你需要计算出给出的 $N$ 个文件的最小访问时间总和。

解题思路

我看了Vijos上的题解表示并没有看懂,于是问问机房的dalao。dalao就是dalao。

这是一道树形DP。首先要明白一个贪心策略。在同一层子目录下,如果现在的剩余空间已经无法容纳剩下的文件那么我们必然是需要在开一个文件夹进行存储的。那这个文件夹开的位置的指针访问时间越小越好。因为如果它是一个文件夹的话,它对答案的影响会随着它的子目录中文件的数量增加而成倍增加。所以要尽量的小。这就是一个贪心的策略,显然在输入的时候是需要对指针的访问时间从小到大排序的。

接下来做树形dp。先看下代码,我们一段一段的讲。

int main() {
    scanf("%d%d", &n, &k);
    for(int i=1; i<=k; i++)
        scanf("%d", &p[i]);
    sort(p+1, p+1+k);
    printf("%d", dp(n, 1, n-1));
}

排序这一部分自然是不用多说了,上面已经说过了。

再来看dp函数

inline int dp(int x, int y, int l) {
    if(x == 1) {
        f[x][y] = p[y];
        return f[x][y];
    }
    if(y == k) {
        f[x][y] = p[y] * x * x + dp(x, 1, x-1);
        return f[x][y];
    }
    int tmp = k-y+1;
    if(tmp * l < x)
        return INF;
    if(f[x][y]) return f[x][y];
    tmp = (x-1)/tmp + 1;
    for(int i=tmp; i<=l; i++) {
        if(i == 1)
            f[x][y] = p[y] + dp(x-1, y+1, x-2);
        else
            f[x][y] = MIN(f[x][y], dp(x-i, y+1, x-i-1) + dp(i, 1, i-1) + p[y] * i * i);
    }
    return f[x][y];
}

$x$ 表示还剩下多少个文件没有被安排。$y$ 表示现在用到的是第 $y$ 个指针。$f[x][y]$ 表示还剩 $x$ 个文件用第 $y$ 个指针的最小的访问时间。

显然如果 $x == 1$ 的话,就直接将现在这个指针的时间给它就好了。如果 $y==k$ 的话,说明 $k$ 个指针已经用到了最后一个那肯定是要建一个文件夹。

这里的 $p[y]*x*x$ 的含义呢,是将这个文件夹的访问时间计算出来并且它之后会产生 $x$ 次影响,将这 $x$ 次影响在这里直接计算出来。

$l$ 表示层数,是最多还能伸下去安排几层。$tmp$ 那个地方是一个小剪枝。如果剩余的内存还不够将这 $x$ 个文件安排好的话就直接反回 $inf$。

往后的状态转移,如果只剩下一个需要安排的话,那就直接安排上。否则的话就开一个文件夹,把除文件夹外的剩余空间进行分配,并且往下深入一层。

附上代码

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn = 1003, INF = 2147483647;
int n, k, p[153], f[maxn][maxn];
inline int MIN(int x, int y) {
    if(!x) return y;
    else return min(x, y);
}
inline int dp(int x, int y, int l) {
    if(x == 1) {
        f[x][y] = p[y];
        return f[x][y];
    }
    if(y == k) {
        f[x][y] = p[y] * x * x + dp(x, 1, x-1);
        return f[x][y];
    }
    int tmp = k-y+1;
    if(tmp * l < x)
        return INF;
    if(f[x][y]) return f[x][y];
    tmp = (x-1)/tmp + 1;
    for(int i=tmp; i<=l; i++) {
        if(i == 1)
            f[x][y] = p[y] + dp(x-1, y+1, x-2);
        else
            f[x][y] = MIN(f[x][y], dp(x-i, y+1, x-i-1) + dp(i, 1, i-1) + p[y] * i * i);
    }
    return f[x][y];
}
int main() {
    scanf("%d%d", &n, &k);
    for(int i=1; i<=k; i++)
        scanf("%d", &p[i]);
    sort(p+1, p+1+k);
    printf("%d", dp(n, 1, n-1));
}
原文地址:https://www.cnblogs.com/bljfy/p/9598616.html