POJ - 1947(树形dp)

题目

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int inf = 0x3f3f3f3f;
#define N 200
int dp[N][N], vis[N], p;
vector<int>vet[N];
void dfs(int x)
{
	int si = vet[x].size();
	for (int k = 0; k < si; k++)
	{
		int v = vet[x][k];
		dfs(v);
		for (int i = p; i > 1; i--)
		{
			for (int j = 1; j < i; j++)
			{
				dp[x][i] = min(dp[x][i], dp[x][i - j] + dp[v][j] - 2);
				//为什么减2,一条是连上son与他的父亲,一条是连上父亲与儿子,因为这两个点之前都各算了一次
			}
		}
	}
}
int main()
{
	int n;
	scanf("%d%d", &n, &p);
	int u, v;
	for (int i = 1; i < n; i++)
	{
		scanf("%d%d", &u, &v);
		vet[u].push_back(v);
		vis[v] = 1;
	}
	int root;
	for (int i = 1; i <= n; i++)
	{
		if (!vis[i])
		{
			root = i;
			break;
		}
	}
	for (int i = 1; i <= n; i++)
	{
		dp[i][1] = vet[i].size() + 1;//大家都加上一个父亲,给根加上虚拟的父亲。。
		for (int j = 2; j <= p; j++)
		{
			dp[i][j] = inf;
		}
	}
	dfs(root);
	dp[root][p]--;//减去虚拟的
	int ans = inf;
	for (int i = 1; i <= n; i++)
	{
		ans = min(ans, dp[i][p]);
	}
	cout << ans << endl;
	return 0;
}



第二种应该好理解点。

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int inf = 0x3f3f3f3f;
#define N 200
int dp[N][N], vis[N], p;
vector<int>vet[N];
void dfs(int x)
{
	int si = vet[x].size();
	for (int k = 0; k < si; k++)
	{
		int v = vet[x][k];
		dfs(v);
		for (int i = p; i > 1; i--)
		{
			for (int j = 1; j < i; j++)
			{
				dp[x][i] = min(dp[x][i], dp[x][i - j] + dp[v][j] - 1);
				//父亲与儿子切断的边连上。
			}
		}
	}
}
int main()
{
	int n;
	scanf("%d%d", &n, &p);
	int u, v;
	for (int i = 1; i < n; i++)
	{
		scanf("%d%d", &u, &v);
		vet[u].push_back(v);
		vis[v] = 1;
	}
	int root;
	for (int i = 1; i <= n; i++)
	{
		if (!vis[i])
		{
			root = i;
			break;
		}
	}
	for (int i = 1; i <= n; i++)
	{
		dp[i][1] = vet[i].size();//只算到儿子的边。
		for (int j = 2; j <= p; j++)
		{
			dp[i][j] = inf;
		}
	}
	dfs(root);
	int ans = dp[root][p];//除了根之外,其他还要切断他与父亲的边,所以其他加上1
	for (int i = 1; i <= n; i++)
	{
		ans = min(ans, dp[i][p] + 1);
	}
	cout << ans << endl;
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值