题单链接
P1352 没有上司的舞会
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxN= 6010 ;
int n, w[ maxN] , head[ maxN] , f[ maxN] [ 2 ] , root, l, k, tot;
bool isroot[ maxN] ;
struct Edge{
int u, v, next;
} e[ maxN] ;
void add ( int u, int v) {
e[ ++ tot] . u= u;
e[ tot] . v= v;
e[ tot] . next= head[ u] ;
head[ u] = tot;
}
void dfs ( int x) {
f[ x] [ 1 ] = w[ x] ;
for ( int i= head[ x] ; i; i= e[ i] . next) {
int y= e[ i] . v;
dfs ( y) ;
f[ x] [ 0 ] + = max ( f[ y] [ 1 ] , f[ y] [ 0 ] ) ;
f[ x] [ 1 ] + = f[ y] [ 0 ] ;
}
}
int main ( ) {
cin>> n;
memset ( isroot, true , sizeof isroot) ;
for ( int i= 1 ; i<= n; i++ ) cin>> w[ i] ;
for ( int i= 1 ; i<= n- 1 ; i++ ) {
cin>> l>> k;
isroot[ l] = false ;
add ( k, l) ;
}
for ( int i= 1 ; i<= n; i++ ) {
if ( isroot[ i] ) root= i;
}
dfs ( root) ;
cout<< max ( f[ root] [ 0 ] , f[ root] [ 1 ] ) << endl;
}
P2015 二叉苹果树
对于每个分支节点来说,有三种选择,(1)减去左子树(2)减去右子树(3)将节点个数合理分配给左右子树。需要在这三种中选取边权和最大的决策。
g
[
x
]
[
k
]
g[x][k]
g [ x ] [ k ] 表示以
x
x
x 为根,含
k
k
k 个节点的子树的最大边权和(包括
x
x
x 通往父节点的边权)。
g
[
x
]
[
k
]
=
{
0
,
k
=
=
0
x
通
往
父
节
点
的
边
权
,
x
为
叶
节
点
x
通
往
父
节
点
的
边
权
+
m
a
x
(
g
[
x
左
儿
子
]
[
i
]
+
g
[
x
右
儿
子
]
[
k
−
1
−
i
]
)
,
x
为
非
叶
节
点
g[x][k]=\left\{ \begin{aligned} 0,k==0\\ x通往父节点的边权,x为叶节点\\ x通往父节点的边权+max(g[x左儿子][i]+g[x右儿子][k-1-i]),x为非叶节点 \end{aligned} \right.
g [ x ] [ k ] = ⎩ ⎪ ⎨ ⎪ ⎧ 0 , k = = 0 x 通 往 父 节 点 的 边 权 , x 为 叶 节 点 x 通 往 父 节 点 的 边 权 + m a x ( g [ x 左 儿 子 ] [ i ] + g [ x 右 儿 子 ] [ k − 1 − i ] ) , x 为 非 叶 节 点
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn= 110 ;
int tot, n, m, q, head[ maxn] , lch[ maxn] , rch[ maxn] , fa[ maxn] , v[ maxn] , g[ maxn] [ maxn] ;
struct Edge{
int u, v, w, next;
} e[ 2 * maxn] ;
void add ( int u, int v, int w) {
e[ ++ tot] . u= u;
e[ tot] . v= v;
e[ tot] . w= w;
e[ tot] . next= head[ u] ;
head[ u] = tot;
}
void dfs ( int x) {
for ( int i= head[ x] ; i; i= e[ i] . next) {
int y= e[ i] . v;
if ( y!= fa[ x] ) {
if ( ! lch[ x] ) lch[ x] = y;
else rch[ x] = y;
fa[ y] = x;
v[ y] = e[ i] . w;
dfs ( y) ;
}
}
}
int dp ( int x, int k) {
if ( k== 0 ) return 0 ;
if ( g[ x] [ k] >= 0 ) return g[ x] [ k] ;
if ( lch[ x] == 0 ) return ( g[ x] [ k] = v[ x] ) ;
for ( int i= 0 ; i< k; i++ ) {
g[ x] [ k] = max ( g[ x] [ k] , dp ( lch[ x] , i) + dp ( rch[ x] , k- 1 - i) ) ;
}
g[ x] [ k] + = v[ x] ;
return g[ x] [ k] ;
}
int main ( ) {
cin>> n>> m;
int x, y, z;
for ( int i= 1 ; i< n; i++ ) {
cin>> x>> y>> z;
add ( x, y, z) ;
add ( y, x, z) ;
}
dfs ( 1 ) ;
for ( int i= 1 ; i<= n; i++ ) {
for ( int j= 1 ; j<= n; j++ ) g[ i] [ j] = - 1 ;
}
cout<< dp ( 1 , m+ 1 ) << endl;
}
P2014 [CTSC1997]选课
将0节点选入,构成了一棵以0节点为根的树。
f
[
i
]
[
j
]
f[i][j]
f [ i ] [ j ] 表示以
i
i
i 为根节点,选取
j
j
j 个节点的最大得分
j
>
0
j>0
j > 0
f
[
i
]
[
j
]
=
m
a
x
(
f
[
i
]
[
j
]
,
f
[
i
]
[
j
−
k
]
+
f
[
v
]
[
k
]
)
,
k
∈
[
1
,
j
−
1
]
f[i][j]=max(f[i][j],f[i][j-k]+f[v][k]),k\in [1,j-1]
f [ i ] [ j ] = m a x ( f [ i ] [ j ] , f [ i ] [ j − k ] + f [ v ] [ k ] ) , k ∈ [ 1 , j − 1 ] 枚举时,由状态更新特点可知
j
j
j 需要逆向枚举.
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn= 310 ;
int n, m, credit[ maxn] , head[ maxn] , f[ maxn] [ maxn] , tot, k;
struct Edge{
int to, next;
} e[ maxn] ;
void add ( int u, int v) {
e[ ++ tot] . to= v;
e[ tot] . next= head[ u] ;
head[ u] = tot;
}
void dfs ( int x) {
f[ x] [ 1 ] = credit[ x] ;
for ( int i= head[ x] ; i; i= e[ i] . next) {
int y= e[ i] . to;
dfs ( y) ;
for ( int j= m; j> 0 ; j-- ) {
for ( int k= 1 ; k< j; k++ ) {
f[ x] [ j] = max ( f[ x] [ j] , f[ x] [ j- k] + f[ y] [ k] ) ;
}
}
}
}
int main ( ) {
cin>> n>> m;
for ( int i= 1 ; i<= n; i++ ) {
cin>> k>> credit[ i] ;
add ( k, i) ;
}
m++ ;
dfs ( 0 ) ;
cout<< f[ 0 ] [ m] << endl;
}
P1613 跑路
G
[
i
]
[
j
]
[
k
]
G[i][j][k]
G [ i ] [ j ] [ k ] 表示从
i
到
j
i到j
i 到 j 是否存在一条长度为
2
k
2^k
2 k 的路径
d
i
s
[
i
]
[
j
]
dis[i][j]
d i s [ i ] [ j ] 代表从
i
到
j
i到j
i 到 j 所需要的最短时间
#include <bits/stdc++.h>
using namespace std;
int dis[ 60 ] [ 60 ] , n, m; bool G[ 60 ] [ 60 ] [ 65 ] ;
void init ( ) {
memset ( G, false , sizeof G) ;
for ( int i= 1 ; i<= 60 ; i++ )
for ( int j= 1 ; j<= 60 ; j++ )
dis[ i] [ j] = 1e9 ;
cin>> n>> m;
for ( int i= 1 ; i<= m; i++ ) {
int x, y; cin>> x>> y;
dis[ x] [ y] = 1 ;
G[ x] [ y] [ 0 ] = true ;
}
for ( int k= 1 ; k<= 64 ; k++ ) {
for ( int i= 1 ; i<= n; i++ ) {
for ( int j= 1 ; j<= n; j++ ) {
for ( int l= 1 ; l<= n; l++ ) {
if ( G[ i] [ l] [ k- 1 ] && G[ l] [ j] [ k- 1 ] ) {
G[ i] [ j] [ k] = true ;
dis[ i] [ j] = 1 ;
}
}
}
}
}
}
void floyd ( ) {
for ( int k= 1 ; k<= n; k++ ) {
for ( int i= 1 ; i<= n; i++ ) {
for ( int j= 1 ; j<= n; j++ ) {
dis[ i] [ j] = min ( dis[ i] [ j] , dis[ i] [ k] + dis[ k] [ j] ) ;
}
}
}
}
int main ( ) {
init ( ) ;
floyd ( ) ;
cout<< dis[ 1 ] [ n] << endl;
}
P1122 最大子树和
f
[
i
]
f[i]
f [ i ] 表示以
i
i
i 为根且包含
i
i
i 的最大子树和
f
[
i
]
+
=
m
a
x
(
0
,
f
[
v
]
)
,
v
f[i]+=max(0,f[v]),v
f [ i ] + = m a x ( 0 , f [ v ] ) , v 是
i
i
i 的孩子
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn= 16010 ;
int n, head[ maxn] , dp[ maxn] , tot, w[ maxn] , f[ maxn] , ans;
struct Edge{
int from, to, next;
} e[ 2 * maxn] ;
void add ( int x, int y) {
e[ ++ tot] . from= x;
e[ tot] . to= y;
e[ tot] . next= head[ x] ;
head[ x] = tot;
}
void dfs ( int x, int fa) {
f[ x] = w[ x] ;
for ( int i= head[ x] ; i; i= e[ i] . next) {
int y= e[ i] . to;
if ( y!= fa) {
dfs ( y, x) ;
f[ x] + = max ( 0 , f[ y] ) ;
}
}
ans= max ( ans, f[ x] ) ;
}
int main ( ) {
cin>> n;
for ( int i= 1 ; i<= n; i++ ) cin>> w[ i] ;
int x, y;
for ( int i= 1 ; i< n; i++ ) {
cin>> x>> y;
add ( x, y) ;
add ( y, x) ;
}
dfs ( 1 , 0 ) ;
cout<< ans<< endl;
}
P2585 [ZJOI2006]三色二叉树
f
[
i
]
[
0
/
1
/
2
]
f[i][0/1/2]
f [ i ] [ 0 / 1 / 2 ] 表示以
i
i
i 为根的子树第
i
i
i 个节点分别涂绿、红、蓝时绿色最多的节点。
#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
const int N= 1e6 ;
int n, ch[ N] [ 2 ] , f[ N] [ 3 ] , g[ N] [ 3 ] , tot;
string s;
int build ( ) {
int now= ++ tot;
if ( s[ now- 1 ] == '2' ) ch[ now] [ 0 ] = build ( ) , ch[ now] [ 1 ] = build ( ) ;
else if ( s[ now- 1 ] == '1' ) ch[ now] [ 0 ] = build ( ) ;
return now;
}
void dfs ( int x) {
int l= ch[ x] [ 0 ] , r= ch[ x] [ 1 ] ;
if ( l) dfs ( l) ; if ( r) dfs ( r) ;
if ( l== 0 && r== 0 ) {
f[ x] [ 0 ] = g[ x] [ 0 ] = 1 ;
f[ x] [ 1 ] = f[ x] [ 2 ] = g[ x] [ 1 ] = g[ x] [ 2 ] = 0 ;
}
f[ x] [ 0 ] = max ( f[ l] [ 1 ] + f[ r] [ 2 ] , f[ l] [ 2 ] + f[ r] [ 1 ] ) + 1 ;
f[ x] [ 1 ] = max ( f[ l] [ 0 ] + f[ r] [ 2 ] , f[ l] [ 2 ] + f[ r] [ 0 ] ) ;
f[ x] [ 2 ] = max ( f[ l] [ 0 ] + f[ r] [ 1 ] , f[ l] [ 1 ] + f[ r] [ 0 ] ) ;
g[ x] [ 0 ] = min ( g[ l] [ 1 ] + g[ r] [ 2 ] , g[ l] [ 2 ] + g[ r] [ 1 ] ) + 1 ;
g[ x] [ 1 ] = min ( g[ l] [ 0 ] + g[ r] [ 2 ] , g[ l] [ 2 ] + g[ r] [ 0 ] ) ;
g[ x] [ 2 ] = min ( g[ l] [ 0 ] + g[ r] [ 1 ] , g[ l] [ 1 ] + g[ r] [ 0 ] ) ;
}
int main ( ) {
cin>> s; n= s. size ( ) ;
dfs ( build ( ) ) ;
cout<< max ( f[ 1 ] [ 0 ] , max ( f[ 1 ] [ 1 ] , f[ 1 ] [ 2 ] ) ) << " " << min ( g[ 1 ] [ 0 ] , min ( g[ 1 ] [ 1 ] , g[ 1 ] [ 2 ] ) ) << endl;
}
P1273 有线电视网
d
p
[
i
]
[
j
]
dp[i][j]
d p [ i ] [ j ] 表示在以
i
i
i 为根的子树中,满足
j
j
j 个客户的需求所能获得的最大收益最终答案为
d
p
[
1
]
[
i
]
>
=
0
dp[1][i]>=0
d p [ 1 ] [ i ] > = 0 的最大的
i
i
i
d
p
[
i
]
[
u
]
[
j
]
dp[i][u][j]
d p [ i ] [ u ] [ j ] 表示在以
u
u
u 为根的子树中,仅考虑前
i
i
i 个孩子,满足
j
j
j 个客户端需求所能获得的最大收益,
d
p
[
i
]
[
u
]
[
j
]
=
m
a
x
(
d
p
[
i
−
1
]
[
u
]
[
j
−
k
]
+
d
p
[
v
的
孩
子
个
数
]
[
v
]
[
k
]
)
,
v
dp[i][u][j]=max(dp[i-1][u][j-k]+dp[v的孩子个数][v][k]),v
d p [ i ] [ u ] [ j ] = m a x ( d p [ i − 1 ] [ u ] [ j − k ] + d p [ v 的 孩 子 个 数 ] [ v ] [ k ] ) , v 是
i
i
i 的第
k
k
k 个孩子。第一维进行优化,
j
j
j 需逆向枚举。因为
i
i
i 是增大的,能够保证
d
p
[
v
]
[
k
]
=
=
d
p
[
v
的
孩
子
个
数
]
[
v
]
[
k
]
dp[v][k]==dp[v的孩子个数][v][k]
d p [ v ] [ k ] = = d p [ v 的 孩 子 个 数 ] [ v ] [ k ]
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N= 3010 ;
int n, m, tot, dp[ N] [ N] , w[ N] , head[ N] ;
struct Edge{
int to, next, w;
} e[ 2 * N] ;
void add ( int u, int v, int w) {
e[ ++ tot] . to= v;
e[ tot] . next= head[ u] ;
e[ tot] . w= w;
head[ u] = tot;
}
int dfs ( int u) {
if ( u> n- m) {
dp[ u] [ 1 ] = w[ u] ;
return 1 ;
}
int sum= 0 ;
for ( int i= head[ u] ; i; i= e[ i] . next) {
int y= e[ i] . to;
int tk= dfs ( y) ;
sum+ = tk;
for ( int j= sum; j> 0 ; j-- ) {
for ( int k= 0 ; k<= min ( j, tk) ; k++ ) {
dp[ u] [ j] = max ( dp[ u] [ j] , dp[ u] [ j- k] + dp[ y] [ k] - e[ i] . w) ;
}
}
}
return sum;
}
int main ( ) {
cin>> n>> m;
for ( int i= 1 ; i<= n; i++ ) {
for ( int j= 1 ; j<= n; j++ ) {
dp[ i] [ j] = - 1e9 ;
}
}
for ( int i= 1 ; i<= n- m; i++ ) {
int size, u, wi; cin>> size;
for ( int j= 1 ; j<= size; j++ ) {
cin>> u>> wi;
add ( i, u, wi) ;
}
}
for ( int i= n- m+ 1 ; i<= n; i++ ) {
cin>> w[ i] ;
}
for ( int i= 1 ; i<= n; i++ ) {
dp[ i] [ 0 ] = 0 ;
}
dfs ( 1 ) ;
for ( int i= m; i> 0 ; i-- ) {
if ( dp[ 1 ] [ i] >= 0 ) {
cout<< i<< endl;
break ;
}
}
}