P1272.重建道路(树形背包)
题意:
给出一棵树。
询问最少断几条边,使得存在大小为P的连通块。
题解:
以每个点为根节点跑树上背包即可。
时间复杂度\(O(n^3)\)
#include<bits/stdc++.h>
using namespace std;
const int maxn=200;
int n,p;
vector<int> g[maxn];
int f[maxn][maxn][2];
int e[maxn];
int sz[maxn];
//f(i,j,k)表示第i个节点所在的连通块的大小为j,且仅考虑前k个儿子
//最多增加的道路数
void dfs (int x,int pre) {
int k=0;
sz[x]=1;
//for (int i=0;i<=n;i++) f[x][i][k]=-1e9;
f[x][1][1]=0;//表示x这个点单独存在,不用加任何边
for (int y:g[x]) {
if (y==pre) continue;
dfs(y,x);
for (int i=0;i<=sz[x];i++) {
for (int j=1;j<=sz[y];j++) {
f[x][i+j][k]=max(f[x][i+j][k],f[x][i][k^1]+f[y][j][e[y]]+1);
f[x][i][k]=max(f[x][i][k],f[x][i][k^1]+f[y][j][e[y]]);
}
}
for (int i=0;i<=sz[x];i++) f[x][i][k]=max(f[x][i][k],f[x][i][k^1]);
k^=1;
sz[x]+=sz[y];
}
e[x]=(k^1);
}
int main () {
scanf("%d%d",&n,&p);
for (int i=2;i<=n;i++) {
int x,y;
scanf("%d%d",&x,&y);
g[x].push_back(y);
g[y].push_back(x);
}
int ans=0;
for (int i=1;i<=n;i++) {
for (int j=0;j<=n;j++) for (int k=0;k<=n;k++) for (int x=0;x<2;x++) f[j][k][x]=-1e9;
dfs(i,0);
ans=max(ans,f[i][p][e[i]]);
}
printf("%d\n",n-1-ans);
}