gfoj 树形dp 重建道路

题目:http://www.gdfzoj.com/oj/contest/271/problems/5

 一场可怕的地震后,人们用N个牲口棚(1≤N≤150,编号1..N)重建了农夫John的牧场。由于人们没有时间建设多余的道路,所以现在从一个牲口棚到另一个牲口棚的道路是惟一的。因此,牧场运输系统可以被构建成一棵树。John想要知道另一次地震会造成多严重的破坏。有些道路一旦被毁坏,就会使一棵含有P(1≤P≤N)个牲口棚的子树和剩余的牲口棚分离,John想知道这些道路的最小数目。

Input

第1行:2个整数,N和P

第2..N行:每行2个整数I和J,表示节点I是节点J的父节点。

Output

单独一行,包含一旦被破坏将分离出恰含P个节点的子树的道路的最小数目。

 

看似仅仅一棵树,实则树形dp

因为N<=150,所以可以f[i][j]:以i为根的子树切掉j个节点

所以就可以从模板适当转换得:

for (j=a[x]-1;j>=1;j--)
	for (k=1;k<=min(j,a[y]);k++)
		f[x][j]=min(f[x][j],f[x][j-k]+f[y][k]);

注意:

1,输出的是f[1][n-p]!!!不是其他!!!

2,特判输入1 1 情况!!!

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>

using namespace std;

int n,p;
vector <int> M[150+5];
int ru[150+5],f[150+5][150+5],a[150+5];

int dfs(int x)
{
	int i,y,sum=1,d;
	
	for (i=0;i<M[x].size();i++)
	{
		y=M[x][i];
		a[y]=dfs(y);
		if (a[y]==0)
			return 0;
		sum+=a[y];
	}
	if (sum==p || sum==n-p)
		return 0;
	else
		return sum;
}

void dfs1(int x)
{
	int i,y,j,k;
	
	for (i=0;i<M[x].size();i++)
	{
		y=M[x][i];
		dfs1(y);
		for (j=a[x]-1;j>=1;j--)
			for (k=1;k<=min(j,a[y]);k++)
				f[x][j]=min(f[x][j],f[x][j-k]+f[y][k]);
	}
	f[x][a[x]]=1;
}

int main()
{
	int i,x,y,fa,ans;
	
	freopen("a.txt","r",stdin);
	scanf("%d%d",&n,&p);
	if (n==p)
	{
		printf("0");
		return 0;
	}
	for (i=1;i<n;i++)
	{
		scanf("%d%d",&x,&y);
		M[x].push_back(y);
		ru[y]++;
	}
	for (i=1;i<=n;i++)
		if (ru[i]==0)
		{
			fa=i;
			break;
		}
	a[fa]=dfs(fa);
	
//	printf("%d\n",a[fa]);
	
	if (a[fa]==0)
	{
		printf("1");
		return 0;
	}
	memset(f,0x3f,sizeof(f));
	for (i=0;i<=n;i++)
		f[i][0]=0;
	dfs1(fa);
	
	ans=0x3f;
//	for (i=1;i<=n;i++)
//	ans=min(f[1][n-p],f[1][p]);
	printf("%d\n",f[1][n-p]);
	
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值