刷题总结——二叉苹果树(ssoj树形dp+记忆化搜索)

题目:

题目背景

URAL:http://acm.timus.ru/problem.aspx?space=1&num=1018

题目描述

有一棵苹果树,如果树枝有分叉,一定是分 2 叉(就是说没有只有 1 个儿子的结点,这棵树共有N 个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是1。 
我们用一根树枝两端连接的结点的编号来描述一根树枝的位置。下面是一颗有 4 个树枝的树:
         2    5
           /   
           3    4
              /
              1 
现在这颗树枝条太多了,需要剪枝。但是一些树枝上长有苹果。
给定需要保留的树枝数量,求出最多能留住多少苹果。

输入格式

第1行2个数,N 和Q(1<=Q<= N,1<N<=100)。N 表示树的结点数,Q 表示要保留的树枝数量。
接下来 N-1 行描述树枝的信息。
每行3个整数,前两个是它连接的结点的编号,第3个数是这根树枝上苹果的数量。
每根树枝上的苹果不超过30000个。

输出格式

一个数,最多能留住的苹果的数量。

样例数据 1

输入  [复制]

 
5 2 
1 3 1 
1 4 10 
2 3 20 
3 5 20

输出

21

题解:

  dp[i][j]:表示i号点所在子树(包括自己)共保留j个点可以留下的最多苹果

  注意这道题我把保留m条边变成保留m+1个点来求的·····

  另外注意在进行树形dp  dfs的时候用记忆化搜索·····

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<cctype>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
const int N=105;
int first[N],next[N*2],go[N*2],val[N*2],tot;
int n,m;
int dp[N][N];
inline void comb(int a,int b,int c)
{
  next[++tot]=first[a],first[a]=tot,go[tot]=b,val[tot]=c;
  next[++tot]=first[b],first[b]=tot,go[tot]=a,val[tot]=c; 
}
inline void dfs(int u,int fa,int Val,int k)
{
  if(u==0||k==0)  
  {
    dp[u][k]=0;
    return;
  }
  if(dp[u][k]!=-1)
    return;
  dp[u][k]=0;
  for(int i=0;i<k;i++)
  { 
    int l=0,r=0,vl,vr;
    for(int e=first[u];e;e=next[e])
    {
      int v=go[e];
      if(v==fa)  continue;   
      if(l==0) 
        l=v,vl=val[e];
      else 
      {  
        r=v,vr=val[e];
        break;
      }
    }
    dfs(l,u,vl,i);
    dfs(r,u,vr,k-i-1);
    dp[u][k]=max(dp[l][i]+dp[r][k-i-1]+Val,dp[u][k]);
  }
  return;
}
int main()
{
  freopen("a.in","r",stdin);
  scanf("%d%d",&n,&m);
  m++;
  memset(dp,-1,sizeof(dp));
  int a,b,c;
  for(int i=1;i<n;i++)
  {
    scanf("%d%d%d",&a,&b,&c);
    comb(a,b,c);
  }
  dfs(1,0,0,m);
  cout<<dp[1][m]<<endl;
  return 0;
}
原文地址:https://www.cnblogs.com/AseanA/p/7400399.html