poj-1947 Rebuilding Roads

31 篇文章 0 订阅
30 篇文章 0 订阅

题意:

给出一颗树,剪去一些边将它变成一个含有m个结点的树;

求减去的最小边数;

1<=m<=n<=150;


题解:

显然是树形dp,考虑状态要包括当前结点信息和子树大小;

就设状态f[ x ][ j ]为在以x为根的子树上,取包括x的j个点,所需要剪掉的边数;

对于x的子结点y,深搜回溯之后的转移为:

f[ x ][ j ]=min(f[ x ][ j-k ]+f[ y ][ k ]);

        0<=k<=size[y]

然而在x有多个子结点的时候会造成前一个dp值过小而无法转移;

这时候应该将f[ x ][ j ]+1之后再转移,因为相对于前一个,剪边数最多+1(即将新结点剪掉);

这道题的状态还是很好想的,就是边界和转移比较坑;

还有最后的答案除了根结点以外,都要+1,因为要从父结点上剪掉这个树;


代码:


#include<vector>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 155
using namespace std;
vector<int>to[N];
int size[N],f[N][N],n,m,ans;
void dfs(int x,int pre)
{
	size[x]=1;
	f[x][0]=1;
	f[x][1]=0;
	int i,j,k,y;
	for(i=0;i<to[x].size();i++)
	{
		if((y=to[x][i])!=pre)
		{
			dfs(y,x);
			size[x]+=size[y];
			for(j=size[x];j>=1;j--)
			{
				f[x][j]++;
				for(k=0;k<=size[y]&&k<j;k++)
				{
					f[x][j]=min(f[x][j-k]+f[y][k],f[x][j]);
				}
			}
		}
	}
	if(size[x]>=m)
		ans=min(ans,f[x][m]+1);
}
int main()
{
	int i,j,k,x,y;
	scanf("%d%d",&n,&m);
	for(i=1;i<n;i++)
	{
		scanf("%d%d",&x,&y);
		to[x].push_back(y);
		to[y].push_back(x);
	}
	ans=0x3f3f3f3f;
	memset(f,0x3f,sizeof(f));
	dfs(1,0);
	ans=min(ans,f[1][m]);
	printf("%d",ans);
	return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值