1. 程式人生 > >「Luogu P2015」二叉蘋果樹 解題報告

「Luogu P2015」二叉蘋果樹 解題報告

題面

一個二叉樹,邊數為n\((2<n\le 100)\),每條邊有一個權值,求剪枝後剩下p\((1<p<n)\)條邊,使p條邊的權值和最大

還看不懂?……

2   5       input:5 2       output:21
 \ /            1 3 1
  3   4         1 4 10
   \ /          2 3 20
    1           3 5 20

能理解了吧!

思路:

樹形打屁DP

很基礎的一道樹形DP

對於每個點,用f[k][i]記錄在以k為根的子樹中,取i條枝的最大值

注意:

1、要注意葉子節點的判斷

2、有可能只取一側的子樹

Code:

#include<bits/stdc++.h>
#define N 110
using namespace std;
int b[N][5],s[N];
int n,p;
int a[N][N],f[N][N];
int read()
{
    int s=0;
    char c=getchar();
    while(!isdigit(c))
        c=getchar();
    while(isdigit(c))
    {
        s=(s<<1)+(s<<3)+c-'0';
        c=getchar();
    }
    return s;
}
void DFS(int k,int fa)
{
    if(s[k]==1)//葉子節點直接返回
        return;
    int i,j;
    int l,r,value;
    l=r=0;
    for(i=1;i<=s[k];i++)//把l兒子和r兒子處理出來,其實不用嚴格區分l和r,因為效果是一樣的
        if(b[k][i]!=fa)
        {
            if(!l)
                l=b[k][i];
            else
                r=b[k][i];
        }
    DFS(l,k);DFS(r,k);
    f[k][1]=max(a[k][l],a[k][r]);//
    for(i=0;i<=p-2;i++)//由於預設要選左枝和右枝,所以i要-2
    {
        f[k][i+2]=max(f[l][i+1]+a[k][l],f[r][i+1]+a[k][r]);//可能直選一邊
        for(j=0;j<=i;j++)//分配左子樹多少,右子樹去多少枝,不用判斷是否能夠取滿,因為取滿的總是最優的
        {//不會影響
            value=f[l][j]+a[k][l]+f[r][i-j]+a[k][r];
            f[k][i+2]=max(f[k][i+2],value);
        }
    }
    return;
}
int main()
{
    int i,x,y;
    n=read();p=read();
    for(i=1;i<n;i++)
    {
        x=read(),y=read(),a[x][y]=read();//矩陣存邊權
        b[x][++s[x]]=y;b[y][++s[y]]=x;//存邊,由於邊數不多,直接用鄰接表存,不用鏈式前向星
    }
    DFS(1,0);//以1為根遍歷數
    printf("%d",f[1][p]);//輸出最後結果
    return 0;
}