BZOJ 1864 三色二叉树
直观感受比较难,因为之前的一道树上染色就把我惊住了,想了好久。
先一步步解决问题,读入就是一个困难—— 如何解决将表达串转化为树呢?
发现不论是“1 S1”还是“2 S1 S2”来说都是有左子树的,并且观察样例可以知道,左子树处理完了以后才处理右子树。所以可以递归建树,由于是二叉树,只需要用ls和rs数组存左右儿子就好了。
那么如何求染成绿色的最大值呢?想到DP。因为只需要染一种颜色,那么我们只需要关注这个点是不是被染成绿色就好了!
定义状态dp ( u,0/1):
dp( u,1)表示这个点被染成绿色,那么子树一定是其他颜色,注意不需要管儿子被染成什么颜色了;所以得到转移方程1:
dp(u,1)= dp(ls ( u),0)+ dp(rs ( u),0)+ 1;
dp ( u,0)表示这个点没有被染成绿色,那么子树一定有一个被染成绿色了,另一个被染成其他颜色了,只是我们不知道到底哪一个被染成了什么颜色;不用担心,因为只会有两种情况,要么左儿子被染成绿色,要么右儿子被染成绿色,我们只需要对这两个情况取最小值(或最大值(根据需要求染绿色的最值决定))所以得到转移方程2:
dp(ls (u),1)+ dp(rs ( u),0) <- 左儿子绿色,右儿子其他色
dp(u,0) min/max
dp(ls (u),0)+ dp(rs ( u),1) <- 右儿子绿色,左儿子其他色
代码:
/**************************************************************
Problem: 1864
User: jerrywans
Language: C++
Result: Accepted
Time:328 ms
Memory:29028 kb
****************************************************************/
#include
const int N = 500000 + 5 ;
int dp [ N << 2 ] [ 3 ] , ls [ N ] , rs [ N ] ;
int tail = 1 ;
int minx ( int a , int b ) {
return a < b ? a : b ;
}
int maxx ( int a , int b ) {
return a > b ? a : b ;
}
void build ( int x ) {
char c ;
scanf ( "%c" , &c ) ;
if ( c == '0' ) return ;
ls [ x ] = ++ tail ;
build ( ls [ x ] ) ;
if ( c == '2' ) {
rs [ x ] = ++ tail ;
build ( rs [ x ] ) ;
}
}
void dfs ( int u , int opt ) {
if ( !u ) return ;
dfs ( ls [ u ] , opt ) ;
dfs ( rs [ u ] , opt ) ;
dp [ u ] [ 1 ] = dp [ ls [ u ] ] [ 0 ] + dp [ rs [ u ] ] [ 0 ] + 1 ;
if ( opt == 1 )
dp [ u ] [ 0 ] = maxx ( dp [ ls [ u ] ] [ 1 ] + dp [ rs [ u ] ] [ 0 ] , dp [ ls [ u ] ] [ 0 ] + dp [ rs [ u ] ] [ 1 ] ) ;
else
dp [ u ] [ 0 ] = minx ( dp [ ls [ u ] ] [ 1 ] + dp [ rs [ u ] ] [ 0 ] , dp [ ls [ u ] ] [ 0 ] + dp [ rs [ u ] ] [ 1 ] ) ;
}
int main ( ) {
build ( 1 ) ;
dfs ( 1 , 1 ) ;
printf ( "%d " , maxx ( dp [ 1 ] [ 0 ] , dp [ 1 ] [ 1 ] ) ) ;
memset ( dp , 0 , sizeof ( dp ) ) ;
dfs ( 1 , 2 ) ;
printf ( "%d" , minx ( dp [ 1 ] [ 0 ] , dp [ 1 ] [ 1 ] ) ) ;
return 0 ;
}