二叉苹果树--树型动态规划T1

来源:JK老班

题目:有一棵苹果树,如果树枝有分叉,一定是分 2 叉(就是说没有只有 1 个儿子的结点)。 这棵树共有 N 个结点(叶子点或者树枝分叉点),编号为 1-N,树根编号一定是 1。

我们用一根树枝两端连接的结点的编号来描述一根树枝的位置。下面是一颗有 4 个树枝 的树:

2   5

  \ /

   3   4

      \ /

       1 

现在这颗树枝条太多了,需要剪枝。但是一些树枝上长有苹果。 给定需要保留的树枝数量,求出最多能留住多少苹果。

 

程序名:apple

输入格式:

第 1 行 2 个数,N 和 Q(1<=Q<= N,1<N<=100)。

N 表示树的结点数,Q 表示要保留的树枝数量。接下来 N-1 行描述树枝的信息。

每行 3 个整数,前两个是它连接的结点的编号。第 3 个数是这根树枝上苹果的数量。 每根树枝上的苹果不超过 30000 个。

输出格式: 一个数,最多能留住的苹果的数量。

输入样例:

5 2

1 3 1

1 4 10

2 3 20

3 5 20

输出样例:

21

来源:URAL(广州六中信息学奥赛小组译)


不能去父留子。

存树,题目没说谁是父亲,谁是孩子,邻接矩阵,邻接链表(数量大时)。

Q=Q左+Q右(Q为边数),Q左:1~Q,Q右:Q-Q左,max{W(Q左)+W(Q右)}。

不从根开始,因为拆下去,会有重复计算。

由叶子往根开始算,树状动态规划,反向层次遍历。


 

import java.util.ArrayList;
import java.util.Scanner;

//邻接矩阵
public class P1559 {

    int N,Q;
    int[][] A;
    
    public P1559() {
        
        //输入
        Scanner sc=new Scanner(System.in); N=sc.nextInt(); Q=sc.nextInt();
        A=new int[N+1][N+1];
        for(int i=1;i<=N;i++) //一些树枝上没有苹果,但是树枝是存在的,不能用0表示没有树枝,用-1表示没有树枝。
            for(int j=1;j<=N;j++) 
                A[i][j]=-1;
        
        for(int i=0;i<N-1;i++) {
            int x=sc.nextInt(), y=sc.nextInt(), weight=sc.nextInt();
            A[x][y]=A[y][x]=weight;
        }
        
        ArrayList[] Children=new ArrayList[N+1];//为了方便,记下孩子
        for(int i=1;i<=N;i++) Children[i]=new ArrayList<Integer>();//每个节点设变长数组,记下孩子
        
        ArrayList<Integer> Que=new ArrayList<Integer>();//队列
        boolean[] Mark=new boolean[N+1];//已经在队列的点不能再入队,作标记
        Que.add(1); Mark[1]=true;//题目:树根编号一定是1
        int front=0;//队首指针
        while(front<Que.size()) {//当队首指针没越过队尾指针,读队首,看有无孩子,将孩子放入队列
            int head=Que.get(front);front++;
            for(int i=1;i<=N;i++)
                if(A[head][i]!=-1 && Mark[i]==false) {
                    Que.add(i); Mark[i]=true;
                    Children[head].add(i);//加入孩子列表,i是head的孩子
                }
        }
        //for(int a:Que) System.out.print(a+" ");
        
        
        //计算
        int[][] Apple=new int[N+1][Q+1];
        for(int i=Que.size()-1;i>=0;i--)//从队尾开始
        {
            int a=Que.get(i);
            if(Children[a].size()!=0) {//非叶节点
                //拿到2个孩子
                int x=(int) Children[a].get(0);
                int y=(int) Children[a].get(1);
                
                
                for(int q=1;q<=Q;q++) {
                    int max=0;
                    for(int q1=0;q1<=q;q1++) {//枚举一下左右子树各分多少边
                        int q2=q-q1; 
                        int current;
                        if(q1==0) 
                            current=Apple[y][q2-1]+A[a][y];//苹果数:左子树不选边,在右子树选q条边,但是要减去父亲连接右子树的那1条边
                        else {
                            current=Apple[x][q1-1]+A[a][x];//苹果数:左子树选q1-1条边(减去父亲连接左子树的那1条边)
                            if(q2>0) current+=Apple[y][q2-1]+A[a][y];//苹果数:加上右子树选q2-1条边(减去父亲连接右子树的那1条边)
                        }
                        
                        if(current>max) max=current;
                    }
                    
                    Apple[a][q]=max;//父亲节点消耗q条边时的苹果数
                    
                }
                
            }
        }
        
        System.out.println(Apple[1][Q]);
    }
    
    
    public static void main(String[] args) {
        P1559 apple=new P1559();
    }

}
 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值