一道qq姐和dtx很早就做了的题www
想学树形dp就是从这道题开始的
然后做了几道题
之后被难住了
于是回来做这道题
首先这道题保证了是一棵二叉树
emmmmm
然后一个dp数组
dp[u][j] 代表保留u节点j条边的最大值
题目保证1为根,那么结果就是dp[1][Q]
状态转移方程:
dp[u][j] = max(dp[u][j],dp[u][j - k - 1] + dp[v][k] + edge[i].weight)
v为u的子节点,k是枚举v保留的边的数
保留j条边的话,其中有一条边是u和v所连的边
所以保留v节点k条边再加上u节点剩下的可以保留的j - 1 - k条边再加上u和v之间的边
状态转移方程真的太难想了www
看看代码吧
#include<cstdio> #include<algorithm> using namespace std; #define maxn 110 struct EDGE{ int nxt,to,weight; }edge[maxn * 2];//因为忘了开二倍所以emmm int head[maxn],cnt; int dp[maxn][maxn]; int n,q; void add(int x,int y,int w){ edge[++cnt].nxt = head[x]; edge[cnt].to = y; head[x] = cnt; edge[cnt].weight = w; } int treedp(int u,int fa){ int num = 0; for(int i = head[u];i;i = edge[i].nxt){ int v = edge[i].to; if(v != fa){//存的双向所以防止死循环 num += treedp(v,u) + 1;//return一个值,子树的边数,再加上相连的一条 for(int j = min(num,q);j > 0;j--) for(int k = min(num,j);k >= 0;k--) dp[u][j] = max(dp[u][j],dp[u][j - k - 1] + dp[v][k] + edge[i].weight); } } return num; } int main(){ scanf("%d%d",&n,&q); for(int i = 1;i < n;i++){ int x,y,w; scanf("%d%d%d",&x,&y,&w); add(x,y,w); add(y,x,w); } treedp(1,0); printf("%d",dp[1][q]); return 0; }
【令人头大
/*
然后最后插一句没什么意义的感慨
我们都是
渺小而平庸的
无名之辈
谢谢你们能在这么短暂的生命之中,记住我的名字
(没错就是看完电影哭的不行之后的伤春悲秋www
*/