【USACO02FEB】Rebuilding Roads 重建道路 题解(树形DP)
题目大意:问使含有$p$个节点的子树分离至少需要去掉几条边。
------------------
设$f[i][j]$表示以$i$为根的子树保留$j$个节点所去掉的最少边数。
初始化$f[u][1]=c[u]$。$c[u]$是这个节点的度。
转移方程$f[u][j]=min(f[u][j],f[u][k]+f[v][j-k]-2)$。为什么要减$2$?这是因为我们在初始化的时候已经把连接父节点和子节点的这条边去掉了。这时候再把他们连起来,为防止重复计算,我们分别把$u->v$和$v->u$的边去掉(代码中是双向连边)。
代码:
//f[u][j]min(f[u][j],f[u][k]+f[v][j-k]-2) //-2:now->to to->now 减去重复的边 初始化的时候已经减掉了 #include<bits/stdc++.h> using namespace std; int c[155],n,p,f[155][205],ans=0x3f3f3f3f; struct node { int next,to; }edge[50005]; int head[50005],cnt; inline void add(int from,int to) { edge[++cnt].next=head[from]; edge[cnt].to=to; head[from]=cnt; } inline int read() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();} while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();} return x*f; } void dfs(int now,int fa) { f[now][1]=c[now]; for (int i=head[now];i;i=edge[i].next) { int to=edge[i].to; if (to!=fa) { dfs(to,now); for (int j=p;j>=1;j--) for (int k=1;k<j;k++) f[now][j]=min(f[now][j],f[now][k]+f[to][j-k]-2); } } } int main() { memset(f,0x3f,sizeof(f)); n=read(),p=read(); for (int i=1;i<n;i++) { int x=read(),y=read(); c[x]++;c[y]++; add(x,y);add(y,x); } dfs(1,0); for (int i=1;i<=n;i++) ans=min(ans,f[i][p]); printf("%d",ans); return 0; }