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\n", dp[scc][m]);
    return 0;
}

 

posted @ 2017-03-17 14:36  19992147  阅读(100)  评论(0编辑  收藏  举报