Binary Apple Tree URAL - 1018 (树形dp)

Let’s imagine how apple tree looks in binary computer world. You’re right, it looks just like a binary tree, i.e. any biparous branch splits up to exactly two new branches. We will enumerate by integers the root of binary apple tree, points of branching and the ends of twigs. This way we may distinguish different branches by their ending points. We will assume that root of tree always is numbered by 1 and all numbers used for enumerating are numbered in range from 1 to N, where N is the total number of all enumerated points. For instance in the picture below N is equal to 5. Here is an example of an enumerated tree with four branches:
2 5
\ /
3 4
\ /
1
As you may know it’s not convenient to pick an apples from a tree when there are too much of branches. That’s why some of them should be removed from a tree. But you are interested in removing branches in the way of minimal loss of apples. So your are given amounts of apples on a branches and amount of branches that should be preserved. Your task is to determine how many apples can remain on a tree after removing of excessive branches.
Input
First line of input contains two numbers: N and Q ( 2 ≤ N ≤ 100; 1 ≤ Q ≤ N − 1 ). N denotes the number of enumerated points in a tree. Q denotes amount of branches that should be preserved. Next N − 1 lines contains descriptions of branches. Each description consists of a three integer numbers divided by spaces. The first two of them define branch by it’s ending points. The third number defines the number of apples on this branch. You may assume that no branch contains more than 30000 apples.
Output
Output should contain the only number — amount of apples that can be preserved. And don’t forget to preserve tree’s root ;-)
Example
input
5 2
1 3 1
1 4 10
2 3 20
3 5 20
output
21

一道树形dp,应该是我真正意义上的一道树规体,做一个转换,我们可以把边上的权值转移到边上,至于是转到这条边上的儿子还是父亲,应该是转移儿子比较好,可以看出如果我们要留下q条边,那么我们可以留下q+1个点,所以我们我们这样设置状态,dp[v][k],含义为,以v为根的子树中还剩下k个点时的最大值,那么有一些很显然的状态,dp[v][0]均为0,dp[v][1]均为v这个点上的值的大小

好,说说状态转移,如果我们需要得到dp[v][k](k>=2)的值,那么我们就需要v的两个儿子的信息(如果有儿子),那么至少有一点我们需要知道v的那些儿子,所以这里,我们需要一个数组son[v][2],表示son[v][0]是son的左儿子,son[v][1]是v的右儿子,如果没有就置为0,因为也没有为0的点,所以转移方程为:
dp[v][k]=max{dp[ son[v][0] ][i],dp[ son[v][1] ][k-1-i] +num[v]}

代码:

#include
#include
#include
#include
using namespace std;
int graph[101][101];//见图
int son[101][2];//表示一个点的两个儿子
int dp[104][104];
int n,q;
int num[104];//每个点的权值
void init()
{
    memset(son,0,sizeof(son));
    memset(dp,-1,sizeof(dp));
    memset(graph,-1,sizeof(graph));
}
void makeTree(int x)//从根开始建树
{
    int k=0;
    for(int i=1;i<=n;i++)
    {
        if(k==2)
            break;
        if(graph[x][i]==-1)
            continue;
        son[x][k++]=i;
        num[i]=graph[x][i];//于此同时把边上的权值转移到点
        graph[i][x]=graph[x][i]=-1;//这个三保证唯一性
        makeTree(i);
    }
}
int dfs(int x,int k)//dfs遍历一下即可
{
    if(k<=1)return dp[x][k];
    if(dp[x][k]!=-1)return dp[x][k];
    if(!son[x][0])
        return dp[x][k]=num[x];
    for(int i=0;i<=k-1;i++)
        dp[x][k]=max(dp[x][k],dfs(son[x][0],i)+dfs(son[x][1],k-1-i));
    return dp[x][k]+=num[x];
}
/*void p(int x)
{
    printf("%d\n",x);
    if(son[x][0])
        p(son[x][0]);
    if(son[x][1])
        p(son[x][1]);
}*/
int main()
{
    init();
    num[1]=0;
    scanf("%d%d",&n,&q);
    q++;
    int x,y,w;
    for(int i=1;iscanf("%d%d%d",&x,&y,&w);
        graph[x][y]=w;
        graph[y][x]=w;//建图
    }
    makeTree(1);//建树
    for(int i=1;i<=n;i++)
    {
        dp[i][1]=num[i];
        dp[i][0]=0;
    }//边界
    dfs(1,q);
    printf("%d\n",dp[1][q]);
}

你可能感兴趣的:(动态规划,树形DP)