隔离村庄
题目描述
有 n 个村庄,通过 n-1 条双向路连通。Jyb 同学财大气粗,想要买其中的恰好 k 个村庄。为了做一些秘密的事情,他想要通过破坏原来的路,在保证自己的 k 个村庄相互连通的情况下,把这 k 个村庄与其他村庄隔离开。请问他应该买哪 k 个村庄,最少破坏多少条路
Solution
表示以结点为子树, 制造大小为联通块所需要断的最少数目边数
则 为转移方程
初值 表示该节点为光棍时的状态,随着子树的遍历, 该值会随着改变,即转移时注意新的子树对先前值的影响, 体现在代码注释处
Code
#include<bits/stdc++.h>
#define reg register
const int maxn = 255;
int N, K, num;
int head[maxn], size[maxn], num_1[maxn], Tmp[maxn], dp[maxn][maxn];
struct Edge{ int nxt, to; } edge[maxn << 1];
void Add(int from, int to){
edge[++ num] = (Edge){ head[from], to };
head[from] = num;
}
void DFS(int k, int fa){
size[k] = 1, num_1[k] = 0;
dp[k][1] = 0;
for(reg int i = head[k]; i; i = edge[i].nxt){
int to = edge[i].to;
if(to == fa) continue ;
DFS(to, k);
size[k] += size[to];
/*
for(reg int j = size[k]; j >= 1; j --)
for(reg int p = 1; p <= size[to]; p ++)
dp[k][j] = std::min(dp[k][j], dp[k][j-p] + dp[to][p]);
*/
for(reg int j = size[k]; j >= 1; j --){
dp[k][j] ++; //新的儿子对过去状态的影响
for(reg int p = 1; p < j; p ++)
dp[k][j] = std::min(dp[k][j], dp[to][j - p] + dp[k][p]);
}
}
}
int main(){
freopen("isolate.in", "r", stdin);
freopen("isolate.out", "w", stdout);
scanf("%d%d", &N, &K);
for(reg int i = 1; i < N; i ++){
int u, v;
scanf("%d%d", &u, &v);
Add(u, v), Add(v, u);
}
memset(dp, 0x3f, sizeof dp);
DFS(1, 0);
/*
int Ans = 0x3f3f3f3f;
for(reg int i = 1; i <= N; i ++) Ans = std::min(Ans, std::min(dp[i][N-K], dp[i][K]));
*/
int Ans = dp[1][K];
for(reg int i = 2; i <= N; i ++) Ans = std::min(Ans, dp[i][K] + 1);
// printf("%d
", dp[2][5]);
printf("%d
", Ans);
return 0;
}