ZOJ 2834 Maximize Game Time

 07年浙大賽的一題。TreeDp。感謝某位不知名牛人。

 

/*
每次遍歷子結點來進行DP過程。
每個結點保留下3個值,
以它爲跟的樹所有結點值的和(結點數)totel,
進行到這個結點的最優值finish,
以及不能走到當前結點,子結點最多可以走的數目。
這樣,每個結點的3個值都可以通過所有子結點的值來確定。
計算完畢後,需要注意的是這題裏的數據可能是一個森林,不在要求樹中的所有結點可以全部加上去。
*/


#include 
<cstdio>
#include 
<string>
#include 
<cstdlib>

#define minLen 1
#define nMax 1001
#define rg(x,y) ( x = x > y ? x : y )

int N;
int root,
total;
int par[nMax],
val[nMax],
sum[nMax],        
// 結點爲根的樹的sum
c[nMax],        // 剛好完成此根時的point
uc[nMax];        // 未完成此根時的最大point

class Son
{
public:
    
int *p;
    
int len;    // 當前子結點數目
    int lMax;    // 數組大小
    Son ()
    
{
        p 
= new int[minLen];
        len 
= 0, lMax = minLen;
    }

    
void Set ()
    
{
        len 
= 0;
    }

    
void Insert ( int x )
    
{
        
if ( len == lMax )
        
{
            
int *t;
            t 
= new int[len << 1];
            memcpy ( t, p, 
sizeof ( int ) * len );
            p 
= t;
            t 
= NULL;
            lMax 
<<= 1;
        }

        p[len 
++= x;
    }

}
;
Son l[
1000];    // 所有節點

int init ()
{
    scanf ( 
"%d"&N );
    
if ( N == 0 )
        
return 0;
    
int i;
    total 
= 0;
    
for ( i = 0; i < N; i ++ )
    
{
        scanf ( 
"%d"&val[i] );
        total 
+= val[i];
        l[i].Set ();
    }

    
for ( i = 0; i < N; i ++ )
    
{
        scanf ( 
"%d"&par[i] );
        
if ( par[i] == -1 )
            root 
= i;
        
else
            l[par[i]].Insert ( i );        
    }

    
return N;
}


void search ( int u )
{
    
int i;
    sum[u] 
= val[u];
    c[u] 
= val[u];
    uc[u] 
= 0;
    
if ( l[u].len == 0 )
        
return;
    
if ( l[u].len == 1 )
    
{
        sum[u] 
+= val[l[u].p[0]];
        c[u] 
+= val[l[u].p[0]];
        uc[u] 
+= val[l[u].p[0]];
        
return;
    }

    
for ( i = 0; i <l[u].len; i ++ )
    
{
        search ( l[u].p[i] );
    }

    
int tSum = 0;
    
for ( i = 0; i < l[u].len; i ++ )
    
{
        sum[u] 
+= sum[l[u].p[i]];
        tSum 
+= uc[l[u].p[i]];
    }

    
int j;
    
// 枚舉2
    int max = 0;
    
int pi, pj;
    
for ( i = 0; i < l[u].len; i ++ )
    
{
        pi 
= l[u].p[i];
        
for ( j = 0; j < l[u].len; j ++ )
        
{
            
if ( i == j )
                
continue;
            pj 
= l[u].p[j];
            
// 第一個用sum[pi]填滿,第二個子結點放入後則立刻轉移至u
            rg ( max, tSum - uc[pi] + sum[pi] - uc[pj] + c[pj] );
        }

    }

    c[u] 
+= max;
    max 
= 0;
    
for ( i = 0; i < l[u].len; i ++ )
    
{
        pi 
= l[u].p[i];
        
// 只能填滿一個子結點
        rg ( max, tSum - uc[pi] + sum[pi] );
    }

    uc[u] 
+= max;
}


int main ()
{
    
//freopen ( "in.txt", "r", stdin );
    
//freopen ( "out.txt", "w", stdout );
    while ( init () )
    
{
        search ( root );
        
// 數據可能是森林
        printf ( "%d ", total - sum[root] + c[root] );
        
//pt ();
    }

    
return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章