hihocoder#1055 : 刷油漆 算法详解以及java源码实现

原题地址详见:http://hihocoder.com/problemset/problem/1055?sid=869767

题目分析:简而言之,就是获得一棵树如果涂连续节点,节点数目一定,最终获得的最大值是多少。

思路:

dp[u][j]表示以节点u为根的大小为 j 的树可得到的最大分数,答案就是dp[1][m]。

状态转移方程为:dp[u][j]=max(dp[v1][k1]+dp[v2][k2]+...+dp[vx][kx]),v是u的子节点。


注意点:

1.边是单向的还是双向的:因为要求是从1开始,我们可以把树设置为单向边,按照节点从大向小的顺序,避免重复遍历,陷入死循环。还有一种递归是根据边来递归的,参考http://www.cnblogs.com/easonliu/p/4468799.html
2.M是正向还是反向遍历?考虑到我们背包问题,物品不可重复使用,类似于这里的节点不可重复使用,我们采用的是用上一状态更新当前状态,而不是更新后的状态更新当前状态,所以选择反向遍历。

源代码如下:
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class CrushRoller {
	public static void main(String args[])
    {
        Scanner in=new Scanner(System.in);
        while(in.hasNextInt())
        {
           int N=in.nextInt();
           int M=in.nextInt();
           //存储每个节点的价值
           int[] V=new int[N+1];
           //dp[i][j]表示存储i节点为根的时候,涂上M个子节点的最大价值
           int[][] dp=new int[N+1][M+1];
           //存储单向边
           List<List<Integer>> map=new ArrayList<List<Integer>>();
           map.add(new ArrayList<Integer>());
           for(int i=1;i<=N;i++)
           {
        	   V[i]=in.nextInt();
        	   map.add(new ArrayList<Integer>());
        	   dp[i][1]=V[i];
           }
           for(int i=1;i<N;i++)
           {
        	   int a=in.nextInt();
        	   int b=in.nextInt();
        	   map.get(a).add(b);
           }
           dfs(dp,1,M,map);
           System.out.println(dp[1][M]);
        }
        in.close();
    }
	
	public static void dfs(int[][] dp,int node,int M,List<List<Integer>> map)
	{
		List<Integer> children=map.get(node);
		//递归调用子树获得子树的dp
		for(int child:children)
		{
			//获得以当前为根的子树的最大dp
			dfs(dp,child,M-1,map);
			
			//类似背包问题从大到小
			for(int i=M;i>1;i--)
			{
				for(int j=1;j<i;j++)
				{
					dp[node][i]=Math.max(dp[node][i],dp[node][i-j]+dp[child][j]);
				}
			}
			
		}
	}
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值