树形dp1
选择结点: dp[ i ] [ 0 / 1]
树上背包:f[ v ] [ k ] =f[ u ] [ k ]+ val;
f [ u ] [ k ]= max( f[v ][k-1],f[u][k])
常规:f[ i ] [ j ],以i为根的结点。。。
http://acm.hdu.edu.cn/showproblem.php?pid=1520
感觉被坑辽,我没看到有多组数据输入欸。
f[ u ][ 0 ] 表示u结点不去,f[ u ] [ 1 ]表示去;
u不去的话,子结点可去可不去;
u去的话,子结点必须去;
注意初始化,以及找根节点。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=6010; 4 const int inf=0x3f; 5 int head[maxn],f[maxn][2],cnt,v1[maxn],vis[maxn]; 6 struct edge{ 7 int nx,to,val; 8 }edge[maxn*2]; 9 void add(int u,int v) 10 { 11 edge[++cnt].nx=head[u]; 12 edge[cnt].to=v; 13 head[u]=cnt; 14 } 15 void dfs(int x) 16 { 17 f[x][1]=v1[x];vis[x]=1; 18 for(int i=head[x];i;i=edge[i].nx) 19 { 20 int v=edge[i].to,w=v1[x]; 21 if(!vis[v]){ 22 dfs(v); 23 f[x][1]+=f[v][0]; 24 f[x][0]+=max(f[v][0],f[v][1]); 25 } 26 } 27 } 28 signed main() 29 { 30 int n; 31 while(scanf("%d",&n)!=EOF){ 32 memset(v1,0,sizeof(v1)); 33 memset(vis,0,sizeof(vis)); 34 memset(edge,0,sizeof(edge)); 35 memset(head,0,sizeof(head)); 36 cnt=0; 37 for(int i=1;i<=n;i++) cin>>v1[i]; 38 int u,v; 39 memset(f,0,sizeof(f)); 40 while((scanf("%d %d",&u,&v),u||v)) 41 { 42 add(u,v);add(v,u); 43 vis[v]=1; 44 } 45 for(int i=1;i<=n;i++) 46 { 47 if(!vis[i]){//找到根节点 48 memset(vis,0,sizeof(vis)); 49 dfs(i); 50 cout<<max(f[i][1],f[i][0])<<endl; 51 break; 52 } 53 } 54 } 55 }
http://acm.hdu.edu.cn/showproblem.php?pid=2196
树上背包:
这类问题就是让你求在树上选一些点满足价值最大的问题,一般都可以设f[i][j]表示i这颗子树选j个点的最优解。
P2014 [CTSC1997]选课
https://www.luogu.com.cn/problem/P2014
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=400; 4 int head[maxn],n,m,val[maxn],cnt,f[maxn][maxn]; 5 struct edge 6 { 7 int nx,to; 8 }edge[maxn*2]; 9 void add(int u,int v) 10 { 11 edge[++cnt].nx=head[u]; 12 edge[cnt].to=v; 13 head[u]=cnt; 14 } 15 void dfs(int x,int fa) 16 { 17 f[x][1]=val[x]; 18 for(int i=head[x];i;i=edge[i].nx) 19 { 20 int v=edge[i].to; 21 if(v!=fa){ 22 dfs(v,x); 23 for(int j=m;j>=0;j--)//树上背包,j为一共选择的结点数 24 { 25 for(int k=0;k<j;k++)//k为子结点选择孙子结点数,j-k则为选择的子结点数 26 f[x][j]=max(f[x][j],f[x][j-k]+f[v][k]); 27 } 28 } 29 } 30 } 31 int main() 32 { 33 cin>>n>>m; 34 for(int i=1;i<=n;i++) 35 { 36 int k; 37 cin>>k>>val[i]; 38 add(k,i),add(i,k); 39 } 40 m++; 41 dfs(0,-1);//设置一个虚根0 42 cout<<f[0][m]<<endl; 43 }
https://www.luogu.com.cn/problem/P2015
转化一下,将边权变为点权;
树上背包模板题;
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=400; 4 int head[maxn],n,m,val[maxn],cnt,f[maxn][maxn]; 5 struct edge 6 { 7 int nx,to,w; 8 }edge[maxn*2]; 9 void add(int u,int v) 10 { 11 edge[++cnt].nx=head[u]; 12 edge[cnt].to=v; 13 head[u]=cnt; 14 } 15 void dfs(int x,int fa) 16 { 17 f[x][1]=val[x]; 18 for(int i=head[x];i;i=edge[i].nx) 19 { 20 int v=edge[i].to; 21 if(v!=fa){ 22 dfs(v,x); 23 for(int j=m;j>0;j--) 24 { 25 for(int k=0;k<j;k++) 26 f[x][j]=max(f[x][j],f[x][j-k]+f[v][k]); 27 } 28 } 29 } 30 } 31 int main() 32 { 33 cin>>n>>m; 34 for(int i=1;i<n;i++) 35 { 36 int k,b,s; 37 cin>>k>>b>>s; 38 if(!val[b]) val[b]=s;//因为给的点不确定是子结点还是父节点,则建立双向边,每个节点只有一个权值 39 else val[k]=s; 40 add(k,b),add(b,k); 41 } 42 m++; 43 dfs(1,0); 44 cout<<f[1][m]<<endl; 45 }