P1272 重建道路
题中说的分离出一颗大小为$p$的子树,其实更好的理解是保留下一颗子树(其实是一个意思)
所以又回到了常见的树上背包模型
令$f[u][j]$表示以u为根的树(不与父亲相连),保留j个节点(包括根)的最小切割次数
有$$f[u][j] = min\left \{ f[u][j-k]+f[v][k]-2 \right \}$$
边界(初始化) $$f[u][1] = deg[u]$$
为什么要$-2$呢$?$
因为一条边在初始化时已被切割两次(父亲一次,儿子一次),但转移时是视为这条边是连通的,所以要$-2$
代码如下
1 #include <iostream> 2 #include <algorithm> 3 #include <cstdio> 4 #include <cstring> 5 #define ll long long 6 using namespace std; 7 8 template <typename T> void in(T &x) { 9 x = 0; T f = 1; char ch = getchar(); 10 while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();} 11 while( isdigit(ch)) {x = 10 * x + ch - 48; ch = getchar();} 12 x *= f; 13 } 14 15 template <typename T> void out(T x) { 16 if(x < 0) x = -x , putchar('-'); 17 if(x > 9) out(x/10); 18 putchar(x%10 + 48); 19 } 20 //------------------------------------------------------- 21 22 const int N = 151; 23 24 int n,p; 25 int sz[N],f[N][N],ans = 1e9+7; 26 27 struct edge { 28 int v,nxt; 29 edge(int v = 0,int nxt = 0):v(v),nxt(nxt){}; 30 }e[N<<1]; int head[N],e_cnt; 31 32 void add(int u,int v) { 33 e[++e_cnt] = edge(v,head[u]); head[u] = e_cnt; 34 } 35 36 void dfs(int u,int fa) { 37 int i,j,k; 38 sz[u] = 1; int deg = (fa ? 1 : 0); 39 for(i = head[u]; i;i = e[i].nxt) { 40 int v = e[i].v; if(v == fa) continue; 41 dfs(v,u); 42 sz[u] += sz[v]; ++deg; 43 } 44 f[u][1] = deg; 45 for(i = head[u]; i;i = e[i].nxt) { 46 int v = e[i].v; if(v == fa) continue; 47 for(j = min(p,sz[u]);j >= 1; --j) { 48 for(k = 1;k <= min(j,sz[v]); ++k) { 49 f[u][j] = min(f[u][j],f[u][j-k]+f[v][k]-2);//notice : -2 50 } 51 } 52 } 53 if(sz[u] >= p) { 54 ans = min(ans,f[u][p]); 55 } 56 } 57 58 int main() { 59 int i,u,v; 60 in(n); in(p); 61 for(i = 1;i < n; ++i) { 62 in(u); in(v); 63 add(u,v); add(v,u); 64 } 65 memset(f,0x3f,sizeof(f)); 66 dfs(1,0); 67 out(ans); 68 return 0; 69 }