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);
} 
posted @ 2021-04-09 14:31  zlc0405  阅读(56)  评论(0编辑  收藏  举报