题目
现在我们的手头有N个软件,对于一个软件i,它要占用Wi的磁盘空间,它的价值为Vi。我们希望从中选择一些软件安装到一台磁盘容量为M计算机上,使得这些软件的价值尽可能大(即Vi的和最大)。
但是现在有个问题:软件之间存在依赖关系,即软件i只有在安装了软件j(包括软件j的直接或间接依赖)的情况下才能正确工作(软件i依赖软件j)。幸运的是,一个软件最多依赖另外一个软件。如果一个软件不能正常工作,那么它能够发挥的作用为0。
我们现在知道了软件之间的依赖关系:软件i依赖软件Di。现在请你设计出一种方案,安装价值尽量大的软件。一个软件只能被安装一次,如果一个软件没有依赖则Di=0,这时只要这个软件安装了,它就能正常工作。
输入格式
第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 )
输出格式
一个整数,代表最大价值
样例输入
3 10
5 5 6
2 3 4
0 1 1
样例输出
5
题解
思路:强连通分量+树形 DP 。
对于每个 i,从 i 向 Di 建一条有向边。
由于依赖关系可以形成环,里面的节点要么都选,要么都不选。
所以,先缩点,构成一个新图,这样新图里的每个节点可以看成一个新图。建一个0节点,向新图里所有的入度为 0 的节点建一条有向边,构成一棵树,以0节点作为根。
建树完毕后,在构成的树上做 DP 。
以下 cost[i] 和 val[i] 分别为树上每个节点的费用和价值,设 f[u][i] 为在节点 u 的子树内,费用限制为 i 的条件下能取到的最大价值。
类似洛谷P2014选课
最后答案为
f[0][m]
f
[
0
]
[
m
]
。
代码
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 105, M = 505;
int n, m, W[N], V[N], f[N][M];
int ecnt, nxt[M], adj[N], go[M], top;
int sta[N], dfn[N], low[N], times;
int num, bel[N], cost[N], val[N], ecnt2;
int nxt2[M], adj2[N], go2[M], d[N];
bool ins[N], G[N][N];
void add_edge(int u, int v)
{
nxt[++ecnt]=adj[u];
adj[u]=ecnt;
go[ecnt]=v;
}
void add_edge2(int u, int v)
{
nxt2[++ecnt2]=adj2[u];
adj2[u]=ecnt2;
go2[ecnt2]=v;
}
void Tarjan(int u)
{
dfn[u]=low[u]=++times;
sta[++top]=u;
ins[u]=1;
for (int e=adj[u],v;e;e=nxt[e])
if (!dfn[v = go[e]])
{
Tarjan(v);
low[u] = min(low[u], low[v]);
}
else if (ins[v]) low[u] = min(low[u], dfn[v]);
if (dfn[u] == low[u]) {
int v; bel[u] = ++num; ins[u] = 0;
while (v = sta[top--], v != u) bel[v] = num, ins[v] = 0;
}
}
void dp(int u)
{
int i, j;
for (i = cost[u]; i <= m; i++)
f[u][i] = val[u];
for (int e = adj2[u], v; e; e = nxt2[e])
{
dp(v = go2[e]);
for (i = m - cost[u]; i >= 0; i--)
for (j = 0; j <= i; j++)
f[u][i + cost[u]] = max(f[u][i + cost[u]],
f[u][i + cost[u] - j] + f[v][j]);
}
}
int main()
{
int i,j,x;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
cin>>W[i];
for(i=1;i<=n;i++)
cin>>V[i];
for(i = 1; i <= n; i++)
if (cin>>x)
add_edge(x, i);
for(i = 1; i <= n; i++)
if (!dfn[i]) Tarjan(i);
for(i = 1; i <= n; i++)
{
cost[bel[i]] += W[i]; val[bel[i]] += V[i];
for (int e = adj[i]; e; e = nxt[e])
if (bel[i] != bel[go[e]]) G[bel[i]][bel[go[e]]] = 1,
d[bel[go[e]]]++;
}
for (i = 1; i <= num; i++)
for (j = 1; j <= num; j++)
if (G[i][j]) add_edge2(i, j);
for (i = 1; i <= num; i++)
if (!d[i])
add_edge2(num + 1, i);
printf("%d\n", (dp(num + 1), f[num + 1][m]));
}