【题解】CF440D Berland Federalization
树形DP,
\(f_{u,i}\) 表示以 \(u\) 为根,取出 \(i\) 个节点子树的最少删边次数。
有
\[f_{u,i}=\min\{f_{u,i-k}+f_{v,k}\}
\]
过程类似分组背包。特别地,当 \(k=0\) 时 \(f_{u,i}\) 强制加 \(1\)。
但题目要输出方案怎么办呢?可以用 \(g_{u,i}\) 记录一下 \(f_{u,i}\) 的后继状态,转移时从 \(g_{u,i-k}\) 复制。后继状态中显然当 \(i=0\) 时 \(u\) 要被删去。
复制后继状态看似给复杂度加了一维,但由于复制时间只与该点的子节点数决定,而子节点数是均摊的,所以不会超时。
注意,当根节点不为 \(1\) 时 ans++。
Code:
#include <bits/stdc++.h>
#define pii pair<int,int>
#define mp make_pair
#define pb push_back
using namespace std;
const int maxn=405;
int n,k;
int id[maxn][maxn];
vector<pii> g[maxn][maxn];
struct node{
int v,nxt;
}edge[maxn*2];int head[maxn],cntE;
void add(int u,int v){
edge[++cntE]=(node){v,head[u]};
head[u]=cntE;
}
int sz[maxn],fa[maxn];
int f[maxn][maxn];
void cpy(int u,int j,int t){
g[u][j].clear();
for(int i=0;i<g[u][t].size();i++){
g[u][j].pb(g[u][t][i]);
}
}
void init(int u,int p){
sz[u]=1;
fa[u]=p;
for(int i=head[u];i;i=edge[i].nxt){
int v=edge[i].v;
if(v==p) continue;
init(v,u);
sz[u]+=sz[v];
}
}
void dfs(int u,int p){
f[u][1]=f[u][sz[u]]=0;
for(int i=head[u];i;i=edge[i].nxt){
int v=edge[i].v;
if(v==p) continue;
dfs(v,u);
for(int j=sz[u];j>=0;j--){
for(int k=0;k<=min(sz[v],j);k++){
if(!k){
f[u][j]++;
g[u][j].pb(mp(v,0));
}
else
if(f[u][j-k]+f[v][k]<f[u][j]){
f[u][j]=f[u][j-k]+f[v][k];
cpy(u,j,j-k);
g[u][j].pb(mp(v,k));
}
}
}
}
}
void get_ans(int u,int w){
for(int i=0;i<g[u][w].size();i++){
if(g[u][w][i].second==0){
if(g[u][w][i].first!=1) printf("%d ",id[u][g[u][w][i].first]);
}else get_ans(g[u][w][i].first,g[u][w][i].second);
}
}
int main(){
scanf("%d%d",&n,&k);
memset(f,0x3f,sizeof f);
for(int i=1;i<=n-1;i++){
int u,v;
scanf("%d%d",&u,&v);
id[u][v]=id[v][u]=i;
add(u,v);add(v,u);
}
init(1,1);
dfs(1,1);
int ans=f[1][k],rt=1;
for(int u=2;u<=n;u++){
if(k>sz[u]) continue;
if(f[u][k]+1<ans){
ans=f[u][k]+1;
rt=u;
}
}
printf("%d\n",ans);
if(rt!=1){
printf("%d ",id[fa[rt]][rt]);
}
if(k!=sz[rt])
get_ans(rt,k);
}

浙公网安备 33010602011771号