树形动态规划

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

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

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

Sample Input
5 2
1 3 1
1 4 10
2 3 20
3 5 20

Sample Output
21


分析:

这题的权值在边上,这在思考时有些别扭,其实只要把边的权值转移到儿子结点上,问题性质不变。
这样状态就应该容易想到了,f[i][j]表示以i结点为根的子树保留j个结点所得的最大值。因为根结点没有权值,所以我们要保留p+1个点。
f[i][j]=max{f[i_left][k]+f[i_right][j-1-k]}   (0<=k<=j-1)
边界 f[i][0]=0;f[i][1]=value[i]
最后f[1][p+1]就是答案


为什么是j-1-k,而不是j-k呢?这是因为要保留以i为根的j个节点,而i已经占了一个节点了,所以孩子节点只能再保留j-1个节点了。

#include<cstring>
#include<cstdio>
#include<iostream>
using namespace std;

struct node
{
    int lc,rc;
    int s;
} tree[220];
int F[110][110],apple[110][110];
bool vis[110];
int n,q;
void make_tree(int root)//一旦涉及到树的操作,递归总是会简化很多代码
{//建立一颗二叉树
    vis[root]=true;
    for (int i=1;i<=n;i++)
        if (!vis[i] && apple[root][i]!=-1)
        {
            if (tree[root].lc==0) tree[root].lc=i;
            else tree[root].rc=i;
            tree[i].s=apple[root][i];
            make_tree(i);
        }
}
int tree_dp(int t,int k)
{
    if (F[t][k]!=-1) return F[t][k];
    if (t==0 || k==0)
    {
        F[t][k]=0; return 0;
    }
    F[t][k]=0;
    for (int i=0;i<=k-1;i++)
    {//递归左右孩子相加即为父亲最优值
        int ls=tree_dp(tree[t].lc,i);
        int rs=tree_dp(tree[t].rc,k-1-i);
        if (F[t][k]<ls+rs) F[t][k]=ls+rs;
    }
    F[t][k]+=tree[t].s;
    return F[t][k];
}
int main()
{
    scanf("%d%d",&n,&q); q++;
    memset(apple,-1,sizeof(apple));
    int a,b,s;
    for (int i=1;i<=n-1;i++)
    {
        scanf("%d%d%d",&a,&b,&s);
        apple[a][b]=s; apple[b][a]=s;
    }
    memset(tree,0,sizeof(tree));
    memset(vis,false,sizeof(vis));
    make_tree(1);
    memset(F,-1,sizeof(F));
    int ans=tree_dp(1,q);
    printf("%d\n",ans);
    return 0;
}


在建树的时候也可以和图算法类似:

#include <iostream>
#include <vector>
using namespace std;
const int maxn=110;
int  N, Q;
int  u, v, w;
int  dp[maxn][maxn];
struct Node
{
    int u, w;
};
vector<Node>adj[maxn];
void init( )
{
    for (int i=1; i<=N; i++) adj[i].clear();
    for (int i=1; i<N; i++)
    {
        scanf("%d%d%d", &u, &v, &w);
        Node temp;
        temp.u = v; temp.w = w;
        adj[u].push_back(temp);
        temp.u = u;
        adj[v].push_back(temp);
    }
}
void  dfs(int root, int father, int left)
{
    if (left<=0) return;
    for (int i=1; i<=Q; i++) dp[root][i] = 0;
    for (int i=0; i<adj[root].size(); i++)
    {
        int u=adj[root][i].u, w=adj[root][i].w;
        if (u!=father && left)
        {
             dfs(u, root, left-1);
             for (int j=left; j>=0; j--)
             {
                 for (int k=0; k+j+1<=left; k++)
                 {
                     dp[root][k+j+1] = max(dp[root][k+j+1], dp[u][k]+dp[root][j]+w);
                 }
             }
        }
    }
    if (left==1) return;
}
int  main( )
{
    while (cin>>N>>Q)
    {
        init( );
        dfs(1, 1, Q); //root is 1
        printf("%d\n", dp[1][Q]);
    }
    return 0;
}

以上参考:

http://hi.baidu.com/%D2%B9%D3%EA552734199/blog/item/f55d742bfccbca20d52af122.html

http://hi.baidu.com/%CA%C0%BD%E7__%CE%D2%B5%C4/blog/item/2fbda2c236b1c55cb219a873.html







  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值