BZOJ 2427: [HAOI2010]软件安装 Tarjan缩点 + DP

Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 1628 Solved: 635

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


这题显然如果有环,我们把其缩成一个点来处理,要么全部买,要么全部不买,然后加一个超级源到所有没有要求的点,然后跑超级源的树上DP就可以了
233调了好久,%黄学长的代码
但是我可以hack黄学长啊,黄学长这组数据要挂233

3 100
5 0 3
0 2 0
0 1 1


#include 
using namespace std;
const int MAXN = 500 + 10;
int timer, dfn[MAXN], low[MAXN], tail, sta[MAXN], top, cnt, scc[MAXN];
int w[MAXN], val[MAXN], head[MAXN], N, M, tmp, sv[MAXN], sw[MAXN], in[MAXN];
int h[MAXN], dp[MAXN][MAXN];
struct Edge{ int to, nxt; } edge[ MAXN * 2 ];
struct Line{ int to, nxt; } line[ MAXN * 2 ];
void add_line( int from, int to ) {
    line[++tail].nxt= head[from];
    line[tail].to = to;
    head[from] = tail;
}
bool vis[MAXN]; 
void Tarjan( int u ) {
    ++timer; dfn[u] = low[u] = timer;
    vis[u] = true; sta[++top] = u;
    for( register int i = head[u]; i; i = line[i].nxt ) {
        int v = line[i].to;
        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] ) {
        cnt++;
        scc[u] = cnt; vis[u] = false;
        sw[cnt] += w[u];
        sv[cnt] += val[u];
        while( sta[top] != u ) {
            vis[sta[top]] = false;
            scc[sta[top]] = cnt;
            sw[cnt] += w[sta[top]];
            sv[cnt] += val[sta[top]];
            top--;
        }
        top--;
    }
}

void insert( int from, int to ) {
    edge[++tail].nxt = h[from];
    edge[tail].to = to;
    in[to] = 1;
    h[from] = tail;
}

bool connec[105][105] ;
void rebuild( ) { tail = 0;
    for( register int i = 1; i <= N; i++ )
        for( register int j = head[i]; j; j = line[j].nxt ) 
            if( scc[line[j].to] != scc[i] && !connec[scc[i]][scc[line[j].to]] )
                insert( scc[i], scc[line[j].to] ) ,
                connec[scc[i]][scc[line[j].to]] = true ;
}
void dpp( int u ) {
    for( register int i = h[u]; i; i = edge[i].nxt ) {
        //printf( "%d to %d\n" , u , edge[i].to ) ; 
        dpp( edge[i].to );
        for( register int j = M - sw[u]; j >= 0; j-- ) 
            for( register int k = 0 ; k <= j ; k++ ) 
            //改法是反向枚举k
                dp[u][j] = max( dp[u][j], dp[u][k] + dp[edge[i].to][ j - k ] );
    }
    for( register int i = M; i >= 0; i-- ) 
        if( i >= sw[u] ) dp[u][i] = dp[u][ i - sw[u] ] + sv[u];
        else dp[u][i] = 0;
}
int main( ) {
    scanf( "%d%d", &N, &M );
    for( register int i = 1; i <= N; i++ ) scanf( "%d", &w[i] );
    for( register int i = 1; i <= N; i++ ) scanf( "%d", &val[i] );
    for( register int i = 1; i <= N; i++ ) { scanf( "%d", &tmp ); if( tmp ) add_line( tmp, i ); }
    for( register int i = 1; i <= N; i++ )
        if( !dfn[i] ) Tarjan( i );
    //printf( "cnt = %d\n" , cnt ) ;
    //printf( "top = %d\n" , top ) ;
    rebuild( );
    //printf( "sv :: %d %d %d\n" , sv[1] , sv[2] , sv[3] ) ;
    for( register int i = 1; i <= cnt; i++ ) 
        if( !in[i] ) insert( cnt + 1, i );
    dpp( cnt + 1 );
    printf( "%d\n", dp[ cnt + 1 ][M] );
    return 0;
}
/*
3 100
5 0 3
0 2 0
0 1 1 
*/

这里写图片描述

你可能感兴趣的:(tarjan,树上DP,磁盘,软件,计算机,2010,dp)