1. 程式人生 > >[洛谷P2015] 二叉蘋果樹

[洛谷P2015] 二叉蘋果樹

題目描述

有一棵蘋果樹,如果樹枝有分叉,一定是分2叉(就是說沒有隻有1個兒子的結點)

這棵樹共有N個結點(葉子點或者樹枝分叉點),編號為1-N,樹根編號一定是1。

我們用一根樹枝兩端連線的結點的編號來描述一根樹枝的位置。下面是一顆有4個樹枝的樹

2   5
 \ / 
  3   4
   \ /
    1

現在這顆樹枝條太多了,需要剪枝。但是一些樹枝上長有蘋果。

給定需要保留的樹枝數量,求出最多能留住多少蘋果。

輸入輸出格式

輸入格式:

第1行2個數,N和Q(1<=Q<= N,1<N<=100)。

N表示樹的結點數,Q表示要保留的樹枝數量。接下來N-1行描述樹枝的資訊。

每行3個整數,前兩個是它連線的結點的編號。第3個數是這根樹枝上蘋果的數量。

每根樹枝上的蘋果不超過30000個。

輸出格式:

一個數,最多能留住的蘋果的數量。

輸入輸出樣例

輸入樣例#1:

5 2
1 3 1
1 4 10
2 3 20
3 5 20

輸出樣例#1:

21

比較基礎的樹形dp,以每個節點及節點對應剪枝數為狀態,容易得出方程

f\left ( u,i \right )=max\left \{ f\left ( v,j \right )+f\left ( u,i-j-1 \right )+w\left ( u,v \right ) \right \}

其中 u,v 表示當前節點及子節點,i j 表示當前狀態還有多少枝條可剪

由於核心部分由遞推實現,j 的更新順序應該從大到小,防止覆蓋

程式碼

#include<cstdio>
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int n,q;
const int N=101;
int d[N][N];
struct edge
{
    int to; int w;
    edge(int v,int s):to(v),w(s){}
};
vector<edge> G[N];
void dp(int u,int fa)
{
    for(int i=0;i<G[u].size();i++)
    {
        edge v=G[u][i]; if(v.to==fa) continue;
        dp(v.to,u);
        for(int i=q;i>=0;i--)
            for(int j=0;j<i;j++)
                d[u][i]=max(d[u][i],d[u][j]+d[v.to][i-j-1]+v.w);
        }
}
int main()
{
    scanf("%d%d",&n,&q);
    for(int i=1;i<n;i++)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        G[u].push_back(edge(v,w));
        G[v].push_back(edge(u,w));
        }
    dp(1,-1);
    printf("%d\n",d[1][q]);
    return 0;
}

 這是我第一次寫的,很沒有條理的搜尋,un記錄了每個節點位於限制狀態下的剪枝狀態,事實上沒必要考慮限制,因為更新過程是從頂到底層層搜尋,不會訪問到不需要的部分

不過這還是有優點的,這是一次漂亮的狀態壓縮啊!(滑稽)

#include<cstdio>
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int n,q,tot=0;
const int N=101;
int d[N][N];
struct edge
{
    int to; int w;
    edge(int v,int s):to(v),w(s){}
};
vector<edge> G[N];
int dp(int u,int fa,int un)
{
    int &ans=d[u][un];
    if(tot>=un) return ans=0;//邊界
    if(ans) return ans;//記憶化
    edge x=edge(0,0); edge y=edge(0,0); 
    for(int i=0;i<G[u].size();i++)
    {
        edge v=G[u][i]; if(v.to == fa) continue;
        if(x.to==0) x=v; else y=v;
        }
    int maxx=0;
    if(x.to!=0) {tot++; maxx=max(maxx,dp(x.to,u,un)+x.w); tot--;}
    if(y.to!=0) {tot++; maxx=max(maxx,dp(y.to,u,un)+y.w); tot--;}
    if(x.to!=0 && y.to !=0 && tot+2 <= un) for(int i=tot;i<=un;i++)
    {
        int ans1=0,ans2=0;
		tot+=1; if(tot <= i) ans1=dp(x.to,u,i)+x.w;tot-=1;
		tot+=1; if(tot <= un-i+tot-1) ans2=dp(y.to,u,un-i+tot-1)+y.w;tot-=1;
		maxx=max(maxx,ans1+ans2);
        }
    return ans=maxx;
}
int main()
{
    scanf("%d%d",&n,&q);
    for(int i=1;i<n;i++)
    {
        int u,v,va;
        scanf("%d%d%d",&u,&v,&va);
        G[u].push_back(edge(v,va));
        G[v].push_back(edge(u,va));
        }
    int ans=dp(1,-1,q);
    printf("%d\n",ans);
    return 0;
}

衍生題目:選課