• bzoj2427


    2427: [HAOI2010]软件安装

    Time Limit: 10 Sec  Memory Limit: 128 MB
    Submit: 1150  Solved: 449
    [Submit][Status][Discuss]

    Description

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

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

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

    Input

    第1行:N, M  (0<=N<=100, 0<=M<=500)
          第2行:W1, W2, ... Wi, ..., Wn (0<=Wi<=M )
          第3行:V1, V2, ..., Vi, ..., Vn  (0<=Vi<=1000 )
          第4行:D1, D2, ..., Di, ..., Dn (0<=Di<=N, Di≠i )

    Output

    一个整数,代表最大价值。

    Sample Input

    3 10
    5 5 6
    2 3 4
    0 1 1

    Sample Output

    5

    HINT

     

    Source

    Day2

    tarjan+背包dp 调了好长时间...

    我们很容易发现一个道理:如果我们选了一个东西,那么它直到它这棵树的根上的东西都要选。而且它们之间的关系可以形成环,如果我们选了环上任意一个东西,那么整个环也的选。

    所以我们可以把一个环缩成一个点,幸运的是这是一个基环树森林,我们用tarjan缩点就行了。

    然后进行树上dp... 我们发现这个东西很像一个背包问题,就是我们当前有m单位空间,用多少空间在一些限制下可以获得最大利益。这个限制就是如果我们选了一个东西,它到它的根上的东西都得选。

    缩完点后是一个森林,于是我们加入一个虚拟顶点作为所有树的根,这样就很方便了。于是我们设定状态dp(i,j)当前选到了i节点,用了j单位空间,所能获得的最大价值。注意,这里的i节点是必须选的,聪明的读者们应该已经知道了。

    于是我们这样转移:先转移每个儿子的状态:dp(i,j) = max(dp(i,j), dp(i,k) + dp(son(i),j - k)))这不难想清楚 但是注意j和k要倒序枚举 因为这里的更新用到了前面的东西,所以要倒序更新。

    最后我们要加上自己的花费: dp(i,j) = dp(i, j - w[i])+v[i] 如果j<w[i]那么dp(i,j)=0,一定要清零,因为这个状态是不合法的。

    #include <bits/stdc++.h>
     
    using namespace std;
     
    const int N = 1010;
     
    struct edge {
        int nxt, to;
    } e[N];
    struct point {
        int w, v;
    } x[N]; 
    vector<int> g[N];
    int n, m, cnt = 1, tot, top, scc;
    int head[N], node[N], in[N], dfn[N], low[N], st[N];
    int dp[N][N], w[N], v[N], vis[N];
    
    void link(int u, int v)
    {
        e[++cnt].nxt = head[u];
        head[u] = cnt;
        e[cnt].to = v;
    }
     
    void tarjan(int u)
    {
        dfn[u] = low[u] = ++ tot; st[++ top] = u;
        vis[u] = 1;
        for(int i = 0; i < g[u].size(); ++i) 
        {
            int v = g[u][i];
            if(!dfn[v]) 
            {
                tarjan(v);
                low[u] = min(low[u], low[v]);
            }
            else if(vis[v]) low[u] = min(low[u], dfn[v]);
        } 
        if(dfn[u] == low[u])
        {
            ++scc;
            int t = 0;
            while(t != u)
            {
                t = st[top--];
                x[scc].w += w[t];
                x[scc].v += v[t];
                vis[t] = 0;
                node[t] = scc;
            }
        }
    }
     
    void dfs(int u)
    {
        for(int i = head[u]; i; i = e[i].nxt) 
        {
            dfs(e[i].to);
            for(int j = m - x[u].w; j >= 0 ; --j)
                for(int k = j; k >= 0; --k) 
                    dp[u][j] = max(dp[u][j], dp[u][k] + dp[e[i].to][j - k]);
        }
        for(int i = m; i >= 0; --i) if(i >= x[u].w) dp[u][i] = dp[u][i - x[u].w] + x[u].v;
        else dp[u][i] = 0;
    }
     
    int main()
    {
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; ++i) scanf("%d", &w[i]);
        for(int i = 1; i <= n; ++i) scanf("%d", &v[i]);
        for(int i = 1; i <= n; ++i) 
        {
            int u; scanf("%d", &u);
            if(u) g[u].push_back(i);
        }
        for(int i = 1; i <= n; ++i) if(!dfn[i]) tarjan(i);
        for(int i = 1; i <= n; ++i)
            for(int j = 0; j < g[i].size(); ++j)
            {
                int v = g[i][j];
                if(node[i] != node[v])
                {
                    link(node[i], node[v]);
                    ++in[node[v]];
                }
            }
        ++scc;
        for(int i = 1; i < scc; ++i) if(!in[i]) link(scc, i);
        dfs(scc);
        printf("%d
    ", dp[scc][m]);
        return 0;
    }
  • 相关阅读:
    Java硬件同步机制Swap指令模拟+记录型信号量模拟
    算法(第四版)练习 1.1.26 ~ 1.1.31
    C++ 电路布线/最短路径问题
    线性代数笔记
    算法导论(第三版)练习 2.2-1 ~ 2.2-4
    条款45: 弄清C++在幕后为你所写、所调用的函数
    条款42: 明智地使用私有继承
    条款41: 区分继承和模板
    【python】字符遍历
    【python】range的用法
  • 原文地址:https://www.cnblogs.com/19992147orz/p/6566003.html
Copyright © 2020-2023  润新知