1. 程式人生 > 其它 >#樹形dp#洛谷 1272 重建道路

#樹形dp#洛谷 1272 重建道路

樹形dp

題目

給出一個大小為 \(n\) 的樹,

問至少斷掉多少條邊使得存在一個大小為 \(m\) 的連通塊

\(n\leq 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);
}