POJ1947(树状DP+背包问题)


/*
 * Cpp0.cpp
 *
 *  Created on: 2014年7月8日
 *      Author: MIAO
 */
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstdio>
using namespace std;

vector<int> V[155];
int dp[155][155];
int n,p;
const int inf=0xffffff;

void dfs(int root)
{
	int i,j,k;
	//先计算完毕所有子树
	for (i=0;i<(int)V[root].size();i++)
		dfs(V[root][i]);

	dp[root][1]=0;
	//类似背包问题的DP,对于第i个子树,拿或者不拿两种情况
	for (i=0;i<(int)V[root].size();i++)
	{
		int son=V[root][i];
		//维护dp[root][1..p]
		for (j=p;j>=1;j--)
		{
			int temp=dp[root][j]+1;//如果不拿第i个子树,则切断这个子树,次数+1

			//如果拿第i个子树,需枚举第i个子树中要取多少个点,假设第i个子树取k个,从上一个状态(第1..i-1个子树)取j-k个,找出最小值
			for (k=1;k<j;k++)
				temp=min(dp[root][j-k]+dp[son][k],temp);
			dp[root][j]=temp;
		}
	}
}

void init()
{
	for (int i=0;i<=n;i++)
		for (int j=0;j<=p;j++)
			dp[i][j]=inf;
}

int main()
{
	int i;
	int degree[155]={0};//入度
	scanf("%d%d",&n,&p);
	init();
	for (i=0;i<n-1;i++)
	{
		int a,b;
		scanf("%d%d",&a,&b);
		V[a].push_back(b);
		degree[b]++;
	}
	//找根
	for (i=1;i<=n;i++)
		if (!degree[i])
			break;
	dfs(i);

	//以每个节点为根,找出最小值
	int ans=dp[i][p];//原来的根,不用+1

	//其它节点要成为根要切断其和父节点的边
	for (i=1;i<=n;i++)
		if (dp[i][p]+1<ans)
			ans=dp[i][p]+1;
	printf("%d\n",ans);
}



顺推要增加一维:

/*
 * Cpp0.cpp
 *
 *  Created on: 2014年7月8日
 *      Author: MIAO
 */
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstdio>
using namespace std;

vector<int> V[155];
int dp[155][155][155];
int n,p;
const int inf=0xffffff;

void dfs(int root)
{
	int i,j,k;
	//先计算完毕所有子树
	for (i=0;i<(int)V[root].size();i++)
		dfs(V[root][i]);

	dp[0][root][1]=0;
	//类似背包问题的DP,对于第i个子树,拿或者不拿两种情况
	for (i=1;i<=(int)V[root].size();i++)
	{
		int son=V[root][i-1];
		int son_size=V[son].size();
		//维护dp[root][1..p]
		for (j=1;j<=p;j++)
		{
			int temp=dp[i-1][root][j]+1;//如果不拿第i个子树,则切断这个子树,次数+1

			//如果拿第i个子树,需枚举第i个子树中要取多少个点,假设第i个子树取k个,从上一个状态(第1..i-1个子树)取j-k个,找出最小值
			for (k=1;k<j;k++)
				temp=min(dp[i-1][root][j-k]+dp[son_size][son][k],temp);
			dp[i][root][j]=temp;
		}
	}
}

void init()
{
	for (int k=0;k<=n;k++)
		for (int i=0;i<=n;i++)
			for (int j=0;j<=p;j++)
				dp[k][i][j]=inf;
}

int main()
{
	int i;
	int degree[155]={0};//入度
	scanf("%d%d",&n,&p);
	init();
	for (i=0;i<n-1;i++)
	{
		int a,b;
		scanf("%d%d",&a,&b);
		V[a].push_back(b);
		degree[b]++;
	}
	//找根
	for (i=1;i<=n;i++)
		if (!degree[i])
			break;
	dfs(i);

	//以每个节点为根,找出最小值
	int ans=dp[V[i].size()][i][p];//原来的根,不用+1

	//其它节点要成为根要切断其和父节点的边
	for (i=1;i<=n;i++)
		if (dp[V[i].size()][i][p]+1<ans)
			ans=dp[V[i].size()][i][p]+1;
	printf("%d\n",ans);
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值