[HAOI2010]软件安装 ---拓扑排序 +树型dp

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_27121257/article/details/83144556

传送门洛谷 P2515


题目描述

现在我们的手头有N个软件,对于一个软件i,它要占用Wi的磁盘空间,它的价值为Vi。我们希望从中选择一些软件安装到一台磁盘容量为M计算机上,使得这些软件的价值尽可能大(即Vi的和最大)。

但是现在有个问题:软件之间存在依赖关系,即软件i只有在安装了软件j(包括软件j的直接或间接依赖)的情况下才能正确工作(软件i依赖软件j)。幸运的是,一个软件最多依赖另外一个软件。如果一个软件不能正常工作,那么它能够发挥的作用为0。

我们现在知道了软件之间的依赖关系:软件i依赖软件Di。现在请你设计出一种方案,安装价值尽量大的软件。一个软件只能被安装一次,如果一个软件没有依赖则Di=0,这时只要这个软件安装了,它就能正常工作。


分析

看起来像是一道裸的树型分组背包来着,结果…
其实按照题目的描述,应该是由内向树(基环树的一种)和普通树组成的森林(如下图),因此要考虑先将森林处理成一棵树,把环缩成点,再进行dp。
在这里插入图片描述
话说网上的都要用tarjan缩点呢,直接拓扑找环不就行了么。
dp的话算是模板了吧,分组背包罢了,参考选课吧。

代码

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <queue>

#define IL inline

using namespace std;

IL int read()
{
    char c = getchar();
    int sum = 0 ,k = 1;
    for(;'0' > c || c > '9'; c = getchar())
        if(c == '-') k = -1;
    for(;'0' <= c && c <= '9'; c = getchar()) sum = sum * 10 + c - '0';
    return sum * k;
}

int fa[105];
int col[105];
int degree[105];
int val[105], weight[105];

int to[205], nxt[205];
int cnt, last[105];
IL void add(int u, int v)
{
    //printf("%d %d\n", u, v);
    to[++cnt] = v; nxt[cnt] = last[u]; last[u] = cnt;
    to[++cnt] = u; nxt[cnt] = last[v]; last[v] = cnt;
}

int f[105][505];
int n, m, S;

IL void topsort()
{
    queue<int>Q;
    
    for(int i = 1; i <= n; ++i)
    if(!degree[i])
        Q.push(i);
    for(int u; !Q.empty();)
    {
        u = Q.front(); Q.pop();
        if(fa[u] && !(--degree[fa[u]])) Q.push(fa[u]); 
    }
}

IL void build()
{
    S = n + 1;
    bool flag = 0;
    for(int i = 1; i <= n; ++i)
    if(degree[i])
    {
        degree[i] = 0;
        for(int x = fa[i]; x != i; x = fa[x])
        {
            --degree[x];
            val[i] += val[x];
            weight[i] += weight[x];
            col[x] = i; 
        }
        fa[i] = 0;
        if(!flag) flag = 1;
    }
    for(int i = 1; i <= n; ++i)
    if(col[i] == i)
    {
        if(fa[i]) add(col[fa[i]], i); else add(S, i);
    }
}

IL int max_(int x, int y) { return x > y ? x : y; }

IL void dfs(int u, int pre)
{
    f[u][weight[u]] = val[u];
    for(int i = last[u], v; (v = to[i]); i = nxt[i])
    if(v != pre)
    {
        dfs(v, u);
        for(int j = m; j >= weight[u]; --j)
        for(int k = 0; j - k >= weight[u]; ++k)
            f[u][j] = max_(f[u][j], f[u][j - k] + f[v][k]);
    }
}

int main()
{
    n = read(); m = read();
    
    for(int i = 1; i <= n; ++i) weight[i] = read();
    for(int i = 1; i <= n; ++i) { col[i] = i; val[i] = read(); }
    for(int i = 1; i <= n; ++i) { fa[i] = read(); if(fa[i]) ++degree[fa[i]];}
    
    topsort();
    build();
    dfs(S, 0);
    printf("%d\n", f[S][m]);
    
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_27121257/article/details/83144556