题目
给出一个大小为 (n) 的树,
问至少断掉多少条边使得存在一个大小为 (m) 的连通块
(nleq 150)
分析
设 (dp[x][s]) 表示以 (x) 为根的子树至少断掉多少条边使得存在一个大小为 (s) 的连通块
则 (dp[x][s]=min{dp[x][s']+dp[y][s-s']})
如果不选该子树则要多断一条边,最后就是
(min{dp[x][m]+[x>1]})
代码
#include <cstdio>
#include <cctype>
#include <cstring>
#define rr register
using namespace std;
const int N=161;
struct node{int y,next;}e[N<<1];
int siz[N],as[N],dp[N][N],n,m,et=1,ans;
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
inline signed min(int a,int b){return a<b?a:b;}
inline void dfs(int x,int fa){
siz[x]=1,dp[x][1]=0;
for (rr int i=as[x];i;i=e[i].next)
if (e[i].y!=fa){
dfs(e[i].y,x),siz[x]+=siz[e[i].y];
for (rr int o=siz[x]-siz[e[i].y];o;--o){
for (rr int j=siz[e[i].y];j;--j)
dp[x][o+j]=min(dp[x][o+j],dp[x][o]+dp[e[i].y][j]);
++dp[x][o];
}
}
}
signed main(){
n=iut(),m=iut(),memset(dp,42,sizeof(dp));
for (rr int i=1;i<n;++i){
rr int x=iut(),y=iut();
e[++et]=(node){y,as[x]},as[x]=et;
e[++et]=(node){x,as[y]},as[y]=et;
}
dfs(1,0),ans=dp[1][m];
for (rr int i=2;i<=n;++i) ans=min(ans,dp[i][m]+1);
return !printf("%d",ans);
}