P1272 重建道路
解题思路
这道题要求我们找到最少需要切断多少条道路,才能分离出一个恰好包含 P 个节点的子树。这是一个典型的树形动态规划问题。
关键思路:
-
树形结构处理:题目给出的是一棵树,我们需要递归处理每个子树。
-
动态规划状态定义:
dp[i][j]表示以节点i为根的子树中,分离出j个节点需要切断的最少道路数。 -
状态转移:对于每个节点,我们考虑它的每个子节点,通过组合不同子树的节点数来更新dp值。
-
初始化:
dp[i][1] = 0,因为不需要切断任何道路就能保留节点i自身。 -
结果计算:最终答案是所有节点中
dp[x][p]的最小值,对于非根节点需要+1(因为要切断它与父节点的连接)。
注意事项:
-
需要处理根节点和非根节点的区别(根节点不需要切断与父节点的连接)
-
在状态转移时要注意遍历顺序(倒序)以避免重复计算
代码注释
#include<bits/stdc++.h> using namespace std; const int N = 1e3 + 10; vector<int> g[N]; // 邻接表存储树结构 int n,p; // n:节点总数, p:需要分离的子树大小 int a[N],f[N],dp[N][N]; // a[i]:以i为根的子树节点数 // f[i]:节点i的父节点 // dp[i][j]:以i为根的子树中分离出j个节点需要的最小切断数 int ans = 1e9; // 存储最终答案,初始化为一个大数 void dfs(int x,int fa) { f[x] = fa; // 记录父节点 a[x] = 1; // 初始化子树大小为1(包含自己) // 遍历所有子节点 for(int i = 0; i < g[x].size(); i++) { int y = g[x][i]; if(y == fa) continue; // 跳过父节点 dfs(y,x); // 递归处理子节点 a[x] += a[y]; // 更新子树大小 // 动态规划处理,倒序更新避免重复计算 for(int j = a[x]; j >= 1; j--) { dp[x][j] += 1; // 初始情况:直接切断与子节点y的连接,代价+1 // 尝试不切断与y的连接,而是从y的子树中取k个节点 for(int k = 1; k <= min(j - 1,a[y]); k++) dp[x][j] = min(dp[x][j], dp[x][j - k] + dp[y][k]); } } // 更新全局答案,如果x不是根节点(1),需要+1(切断与父节点的连接) ans = min(ans, dp[x][p] + (x != 1)); } int main() { cin >> n >> p; // 读入树结构 for(int i = 1; i < n; i++) { int x,y; cin >> x >> y; g[x].push_back(y); g[y].push_back(x); } memset(dp,0x3f,sizeof(dp)); // 初始化DP数组为极大值 for(int i = 1; i <= n; i++) dp[i][1] = 0; // 分离出1个节点(自己)不需要切断任何边 dfs(1,0); // 从根节点1开始DFS遍历 cout << ans; return 0; }

浙公网安备 33010602011771号