P2015 二叉蘋果樹

自為風月馬前卒發表於2017-06-15

題目描述

有一棵蘋果樹,如果樹枝有分叉,一定是分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的題目,
剛開始的時候用貪心亂搞,搞了搞邊,搞了搞點,搞了顆樹,搞了39分。。。
  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 using namespace std;
  6 const int MAXN=1001;
  7 int n,q;
  8 int v[MAXN];
  9 int vis[MAXN];
 10 int deep[MAXN];
 11 int read(int & n)
 12 {
 13     char p='+';int x=0;
 14     while(p<'0'||p>'9')
 15         p=getchar();
 16     while(p>='0'&&p<='9')
 17     x=x*10+p-48,p=getchar();
 18     n=x;
 19 }
 20 struct node
 21 {
 22     int u;
 23     int v;
 24     int w;
 25     int nxt;
 26 }edge[MAXN];
 27 int num=1;
 28 int head[MAXN];
 29 void add_edge(int x,int y,int z)
 30 {
 31     edge[num].u=x;
 32     edge[num].v=y;
 33     edge[num].w=z;
 34     edge[num].nxt=head[x];
 35     head[x]=num++;
 36 }
 37 struct deal
 38 {
 39     int how;
 40     int val;
 41     int l;
 42 }a[MAXN];
 43 void build_tree(int p)
 44 {
 45     vis[p]=1; 
 46     for(int i=head[p];i!=-1;i=edge[i].nxt)
 47     {
 48         if(vis[edge[i].v]==0)
 49         {
 50             deep[edge[i].v]=deep[p]+1;
 51             build_tree(edge[i].v);
 52         }
 53         
 54     }
 55 }
 56 void deal_val(int p,int pre)
 57 {
 58     vis[p]=1;
 59     for(int i=head[p];i!=-1;i=edge[i].nxt)
 60     {
 61         int will=edge[i].v;
 62         if(will!=0&&vis[will]==0&&deep[will]>deep[p])
 63         {
 64             a[pre].l++;
 65             a[pre].val+=edge[i].w;
 66             deal_val(will,pre);
 67         }
 68         
 69         
 70     }
 71 }
 72 int comp(const deal & a ,const deal & b)
 73 {
 74     if(a.val!=b.val)
 75     return a.val<b.val;
 76     return edge[head[a.how]].w<edge[head[b.how]].w;
 77 }
 78 int main()
 79 {
 80     int ans=0;
 81     read(n);read(q);
 82     q=n-q;
 83     for(int i=1;i<=n;i++)
 84         head[i]=-1,a[i].how=i;
 85     for(int i=1;i<=n-1;i++)
 86     {
 87         int x,y,z;
 88         read(x);read(y);read(z);
 89         ans=ans+z;
 90         add_edge(x,y,z);
 91         add_edge(y,x,z);
 92     }
 93     deep[1]=1;
 94     build_tree(1);
 95     for(int i=1;i<=n;i++)
 96     {
 97         memset(vis,0,sizeof(vis));
 98         deal_val(i,i);
 99     }
100         
101     sort(a+1,a+n+1,comp);
102     
103     for(int i=1;i<=q-1;i++)
104         ans=ans-edge[head[a[i].how]].w;    
105     cout<<ans;
106     return 0;
107 }
貪心

後來看題解用樹形DP,

對於每一個節點,我們可以列舉它相連的邊,

用dp[i][j]表示第i個節點,在他的子節點中保留j條樹枝的最大值

那麼我們可以列舉節點和j,當然,在列舉j的時候我們需要同時列舉一個k

我們可以想一下,該節點i需要保留j條邊,那麼我們取他的最大值得時候,可以對他相連的節點進行DP,

如果j=5,這時候需要保留5條邊,

因為整個樹是二叉樹,所以說,他的相連的一條邊所能刪除的最大數就是4(相連的線段一定會刪除)

那麼k的範圍:0--4

動態轉移方程:   

dp[u][j]=max(dp[u][j],dp[u][j-k-1]+dp[will][k]+edge[i].w);

 

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 const int MAXN=1001;
 7 int n,q;
 8 int v[MAXN];
 9 int vis[MAXN];
10 int deep[MAXN];
11 int dp[MAXN][MAXN];
12 int read(int & n)
13 {
14     char p='+';int x=0;
15     while(p<'0'||p>'9')
16         p=getchar();
17     while(p>='0'&&p<='9')
18     x=x*10+p-48,p=getchar();
19     n=x;
20 }
21 struct node
22 {
23     int u;
24     int v;
25     int w;
26     int nxt;
27 }edge[MAXN];
28 int num=1;
29 int head[MAXN];
30 void add_edge(int x,int y,int z)
31 {
32     edge[num].u=x;
33     edge[num].v=y;
34     edge[num].w=z;
35     edge[num].nxt=head[x];
36     head[x]=num++;
37 }
38 int dfs(int u,int fa)
39 {
40     int ans=0;
41     for(int i=head[u];i!=-1;i=edge[i].nxt)
42     {
43         int will=edge[i].v;
44         if(will==fa)
45             continue;
46         ans+=dfs(will,u)+1;
47         for(int j=min(q,ans);j>=1;j--)
48         {
49             for(int k=min(j,ans)-1;k>=0;k--)
50             {
51                 dp[u][j]=max(dp[u][j],dp[u][j-k-1]+dp[will][k]+edge[i].w);
52             }
53         }
54     }
55     return ans; 
56 }
57 int main()
58 {
59     read(n);read(q);
60     for(int i=1;i<=n;i++)
61         head[i]=-1;
62     for(int i=1;i<=n-1;i++)
63     {
64         int x,y,z;
65         read(x);read(y);read(z);
66         add_edge(x,y,z);
67         add_edge(y,x,z);
68     }
69     dfs(1,0);
70     cout<<dp[1][q];
71     return 0;
72 }

 

相關文章