树形DP入门
树形DP都有啥?(谁说对了就教他)
ANSWER:在树上跑的DP……(逃)。
(别打我)事实上,树形DP本质上就是一般的DP,不同的,他把链上动态规划状态拓展成平面上的树;
因此,他的策略应该跟一般的DP一样的说。
当然,他的方向毕竟要跟链表不同,一般的树上DP多是从叶子或给定的根开始你的状态。
直接上例题!
P2015 二叉苹果树(板子) 题目链接:https://www.luogu.org/problem/P2015
一句话题意:给定一棵树,求出上面权值最大的树的权值。
了解到如果把这个边保留下来,那么他的父点以上向根连的边必然也选。
因此我们可以从叶子向上拓展子树进行动态规划。
设dp[u][j]表示以u为根,选了j个边的最大权值;
设计出状态:dp[u][j]=max(dp[u][j],dp[u][j-k-1]+dp[v][k]+ed[i].val);
其中后半部分表示砍去。
剩下的就是建树与建边得斯!( ̄▽ ̄)/
代码如下:
1 #include<bits/stdc++.h>//动规 2 using namespace std; 3 #define maxn 1005 4 struct edge{ 5 int nxt,to,val; 6 }ea[maxn]; 7 int n,cnt,sum,head[maxn],dp[maxn][maxn],en[maxn],q,k; 8 bool vis1[maxn],vis2[maxn]; 9 inline int read(){ 10 int a=1,b=0;char t; 11 while(t<'0'||t>'9'){ 12 if(t=='-') a=-1; 13 t=getchar(); 14 } 15 while(t>='0'&&t<='9') b=b*10+t-'0',t=getchar(); 16 return a*b; 17 } 18 inline void write(int x){ 19 if(!x) putchar('0'); 20 else{ 21 int dig[35],top=0; 22 while(x) dig[++top]=x%10,x/=10; 23 while(top) putchar(dig[top--]+'0'); 24 } 25 } 26 void add_edge(int u,int v,int z){ 27 ea[++cnt].to=v,ea[cnt].nxt=head[u],head[u]=cnt,ea[cnt].val=z; 28 ea[++cnt].to=u,ea[cnt].nxt=head[v],head[v]=cnt,ea[cnt].val=z; 29 } 30 void dfs(int u,int fa){ 31 for(int i=head[u];i;i=ea[i].nxt){ 32 int v=ea[i].to; 33 if(v!=fa){ 34 dfs(v,u); 35 en[u]+=en[v]+1; 36 for(int j=min(en[u],q);j;j--) 37 for(int k=min(j-1,en[v]);k>=0;k--)//手残顺手打了一个k,应该是k>=0(k==0刚好到) 38 dp[u][j]=max(dp[u][j],dp[u][j-k-1]+dp[v][k]+ea[i].val); 39 } 40 } 41 } 42 int main(){ 43 n=read(),q=read(); 44 for(int i=1,u,v,z;i<n;i++){ 45 u=read(),v=read(),z=read(); 46 add_edge(u,v,z); 47 } 48 dfs(1,-1); 49 write(dp[1][q]); 50 }
Cogito ergo sum