题目描述
有一棵苹果树,如果树枝有分叉,一定是分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,以每个节点及节点对应剪枝数为状态,容易得出方程
其中 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;
}
衍生题目:选课