动态规划:P2015二叉苹果树 树形DP 分组背包
P2015二叉苹果树
题目:
思路分析:
观察题目可以看出,这是一个有边权的二叉树。找到一颗子树,使边的个数满足题目限定的数量,并且边权和最大。显然是树形DP 要用分组背包的思路,构造二维DP数组,dp[i][j]代表的时候以i为根节点,连接j个枝条的时候,的总权值和。但是注意分组背包的时候,对于每一结点,利用分组背包DP模板时,要考虑取值范围,j的范围是min(m,size[本结点]),size数组的意思就是从上往下搜,这个结点向下连的总枝条数,这里有个技巧,不用再一个函数来计算连接的枝条数,直接在dfs里计算,在for循环遍历邻接点时,dfs邻接点后,size(根节点)+=size(邻接点)+1,+1就是+根节点和邻接点的枝条,因为dfs邻接点在计算size之前,所以size(邻接点)一定是算出来的,考虑叶子结点,叶子结点不进行for循环,枝条数为0,因为叶子结点向下连的枝条数为0,显然这样做是可以的。
关键DP代码:
总代码:
1 #include<iostream> 2 #include<algorithm> 3 #include<vector> 4 using namespace std; 5 const int maxn = 3 * 1e2 + 5; 6 const int inf = 0x7fffffff; 7 int n, m; 8 int dp[maxn][maxn];//表示以i为起点 留j个枝条 的权值和 9 int head[maxn], val[2 * maxn], to[2 * maxn], nex[2 * maxn], index; 10 int siz[maxn]; 11 void add(int a, int b, int c) 12 { 13 index++; 14 to[index] = b; 15 nex[index] = head[a]; 16 head[a] = index; 17 val[index] = c; 18 } 19 void dfs(int u, int fa) 20 { 21 for (int i = head[u]; i; i = nex[i]) 22 { 23 int x = to[i]; 24 if (x == fa)continue;//避免向上查找 25 dfs(x, u); 26 siz[u] += (siz[x] + 1);//最大能连接的枝条数 关键 27 //开始01背包 28 for (int j = min(m, siz[u]); j >= 1; --j)//j最大只能到m 所以要用min函数 j最小也得取1 29 { 30 for (int k =0; k <= min(j - 1, siz[x]); ++k)//k最多只能到j-1 因为从根开始 第一个必须要 也就是u结点必须选 31 { 32 dp[u][j] = max(dp[u][j], dp[u][j - k - 1] + dp[x][k] + val[i]);//注意j-k-1,因为连接u 和x还有一个树枝 33 // cout << u << " " << j << ":" << dp[u][j] << endl << endl; 34 } 35 } 36 37 } 38 } 39 int main() 40 { 41 cin >> n >> m; 42 for (int i = 1; i < n; ++i) 43 { 44 int a, b, c; 45 cin >> a >> b >> c; 46 add(a, b, c); 47 add(b, a, c); 48 } 49 dfs(1, -1); 50 cout << dp[1][m]; 51 return 0; 52 }
通过: