树形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 }
View Code

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 }
View Code

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 }
View Code

 

posted @ 2020-07-06 23:12  SuccessfulRoad  阅读(122)  评论(0编辑  收藏  举报