2427: [HAOI2010]软件安装
Time Limit: 10 Sec Memory Limit: 128 MB
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
思路:
根据题目,我们建立图。
显然这个图由一些树和一些scc构成(注意:scc一定不在树上),那么我们可以知道,如果选了scc中的一个点,其他点必须也要选,所以我们把所有的scc缩成一个点,这样就构成了一个森林。
对于一个入度为0的点,我们从一个虚点向其连接一条边,这样图就变成了树。
考虑树形dp,定义f [ i ][ j ]为对于i为根的子树总共分配j点权值能拿到的最大value。按照拓扑序做背包就行了。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#define N 505
#define INF 0x3f3f3f3f
using namespace std;
struct Edge{
int to, next;
}ed1[2 * N], ed2[2 * N];
int idc1, head[N], idc2, head2[N], in[N];
void adde1( int u, int v ) {
ed1[++idc1].to = v;
ed1[idc1].next = head[u];
head[u] = idc1;
}
void adde2( int u, int v ) {
in[v] = 1;
ed2[++idc2].to = v;
ed2[idc2].next = head2[u];
head2[u] = idc2;
}
int idx, dfn[N], low[N], ins[N], stack[N], top;
int n, m, w[N], v[N], x, f[105][505];
int scc, sv[N], sw[N], place[N];
void tarjan( int u ) {
dfn[u] = low[u] = ++idx;
ins[u] = 1;
stack[++top] = u;
for ( int i = head[u]; i; i = ed1[i].next ) {
int v = ed1[i].to;
if ( !dfn[v] ) {
tarjan(v);
low[u] = min(low[u], low[v]);
}
else if (ins[v]) low[u] = min(low[u], dfn[v]);
}
if( low[u] == dfn[u] ) {
scc++; int now = 0;
while( now != u ) {
now = stack[top--];
ins[now] = 0;
place[now] = scc;
sv[scc] += v[now];
sw[scc] += w[now];
}
}
}
void dp( int u ) {
for ( int i = 0; i < sw[u]; i++ ) f[u][i] = 0;
for ( int i = sw[u]; i <= m; i++ ) f[u][i] = sv[u];
for ( int i = head2[u]; i; i = ed2[i].next ) {
int v = ed2[i].to;
dp( v );
for ( int k = m; k >= sw[u]; k-- )
for ( int j = 0; j <= k-sw[u]; j++ )
f[u][k] = max(f[u][k], f[u][k-j]+f[v][j]);
}
}
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++ ) {
scanf( "%d", &x );
if ( x != 0 )
adde1( x, i );
}
for ( int i=1; i<=n; i++ )
if ( !dfn[i] ) tarjan(i);
for ( int i=1; i<=n; i++ )
for ( int j=head[i]; j; j=ed1[j].next ) {
int v = ed1[j].to;
if ( place[v] != place[i] )
adde2(place[i], place[v]);
}
for ( int i=1; i<=scc; i++ )
if ( !in[i] ) adde2(scc+1, i);
dp(scc+1);//虚点
printf( "%d\n", f[scc+1][m] );
return 0;
}