ACM:最大子树 动态规划题 toj2676

                    Tree of Tree

描述

 

You're given a tree with weights of each node, you need to find the maximum subtree of specified size of this tree.

Tree Definition
A tree is a connected graph which contains no cycles.

输入

 

There are several test cases in the input.

The first line of each case are two integers N(1 <= N <= 100), K(1 <= K <= N), where N is the number of nodes of this tree, and K is the subtree's size, followed by a line with N nonnegative integers, where the k-th integer indicates the weight of k-th node. The following N - 1 lines describe the tree, each line are two integers which means there is an edge between these two nodes. All indices above are zero-base and it is guaranteed that the description of the tree is correct.

输出

 

One line with a single integer for each case, which is the total weights of the maximum subtree.

样例输入

3 1
10 20 30
0 1
0 2
3 2
10 20 30
0 1
0 2

样例输出

30
40

 

题意: 给你一棵树, 每个节点有一个相应的权值. 并且给定确定的子树节点个数, 求出最大的子树.

 

解题思路:

               1. 一开始的思路只想到暴力深搜枚举, 不断更新最大值.

               2. 发现深搜时, 深度过大递归树无法完成任务.

               3. 网上看题解发现, 是dfs + dp规划.

               4. 动态规划:

                问题分析:

                       (1). 找状态转移的状态:  dp[curNode][k]: 表示当前第curNode个节点, 到子树有k个

                              节点的最大权值.

                       (2). 状态转移方程: dp[curNode][k] = max(dp[curNode][k], dp[curNode][j] + dp[curNode][k-j]

 

代码:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
#define MAX 205

struct node
{
 int u, v;
 int next;
}edges[MAX];

int w[MAX], first[MAX];
int n, k;
int maxsize;
int num;
int dp[MAX][MAX];  //dp[i][k]: 表示第i个节点开始的k个节点最大权值

inline int max(int a,int b)
{
 return a > b ? a : b;
}

void read_graph()
{
 maxsize = 0;
 memset(first,-1,sizeof(first));
 memset(edges,0,sizeof(edges));
 num = 0;
 int i;
 for(i = 0; i < n; ++i)
 {
  scanf("%d",&w[i]);
 }

 int u, v;
 for(i = 0; i < n-1; ++i)
 {
  scanf("%d %d",&u,&v);
  edges[num].u = u;
  edges[num].v = v;
  edges[num].next = first[u];
  first[u] = num++;
 }
}

void find(int curNode,int child)
{
 for(int i = k; i >= 1; --i)
 {
  for(int j = i; j >= 1; --j)
  {
   dp[curNode][i] = max(dp[curNode][i] , dp[curNode][j] + dp[child][i-j]); //更新当前节点curNode, i个子节点的最大权值
  }
 }
}

void dfs(int curNode,int parent)
{
 dp[curNode][1] = w[curNode];
 for(int e = first[curNode]; e != -1; e = edges[e].next)
 {
  int child = edges[e].v;
  if(child == parent)
   continue;
  dfs(child,curNode);
  find(curNode,child);
 }
}

int main()
{
// freopen("input.txt","r",stdin);
 while(scanf("%d %d",&n,&k) != EOF)
 {
  read_graph();

  memset(dp,0,sizeof(dp));
  dfs(0,0);

  maxsize = -1;
  for(int i = 0; i < n; ++i)
   maxsize = max(maxsize,dp[i][k]);

  printf("%d\n",maxsize);
 }
 return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值