树形DP——1272重建道路
树形DP——1272重建道路
这个题目dp【i】【j】代表以 i 这个节点为根节点的子树包含 j 个节点(包括 i 这个节点)总共要删去多少边。其中这里的dp转移式子是dp【u】【j】 = min(dp【u】【j-k】+dp【v】【k】-1)这里为什么要-1呢因为初始化的时候是用出度来优化dp【i】【1】,然后转移的时候加入了以v为根的子树的最优结果。但如果必须要u和v这条边,所以最后会-1.

同时为什么这里是从sum开始倒着遍历,如果是正着遍历,那么后面的dp【u】【j-k】就会用到之前更新的数据。那为什么不能用之前更新的数据呢。因为在之前更新是算入了加入了v这个子树的结果,如果再算加入一遍v这个节点的子树那肯定重复错误了
当时我错的另外一个如果选择的节点不是根节点要+1,这里是因为我们算dp【i】【j】的时候是只考虑了以i这个节点为根节点的情况,但是其实如果不是根节点。i 还会和自己的父亲连接,那么就需要多断掉 i 和 i 的父亲的节点的边,那么答案要加1.
#include<iostream> #include<string> #include<vector> #include<algorithm> #include<cstdio> using namespace std; const int MAXN = 150+10; struct Edge{ int v, next; }edge[MAXN]; int head[MAXN]; int cnt; void addEdge(int u, int v) { edge[cnt].v = v; edge[cnt].next = head[u]; head[u] = cnt++; return; } int outDegree[MAXN], inDegree[MAXN]; int dp[MAXN][MAXN]; void ini(int n) { for(int i = 1; i <= n; i++) head[i] = -1; for(int i = 1; i < MAXN; i++) for(int j = 1; j < MAXN; j++) dp[i][j] = MAXN; cnt = 0; return; } int dfs(int u) { int temp;int sum = 1; for(int i = head[u]; ~i; i = edge[i].next) { int v = edge[i].v; temp = dfs(v); sum += temp; for(int j = sum; j >= 1; j--) { for(int k = 1; k < j; k++) dp[u][j] = min(dp[u][j], dp[u][j-k]+dp[v][k]-1); } } return sum; } int main() { int n, p; scanf("%d %d", &n, &p); ini(n); int u, v; int root; for(int i = 1; i < n; i++) { scanf("%d %d", &u, &v); addEdge(u, v); outDegree[u]++; inDegree[v]++; } for(int i = 1; i <= n; i++) if(inDegree[i] == 0) root = i; for(int i = 1; i <= n; i++) dp[i][1] = outDegree[i]; dfs(root); int ans = dp[root][p]; for(int i = 1; i <= n; i++) { if(dp[i][p] < ans) { ans = dp[i][p]; ans++; } } cout << ans; return 0; }

浙公网安备 33010602011771号