树形dp题集(陆续添加)

本文深入探讨了树形DP算法的应用,通过两道经典题目详细解释了如何利用DP解决树结构上的最优化问题,包括最小着色节点数和最大独立集问题。

1.题目链接:https://ac.nowcoder.com/acm/problem/19914

 

题目描述

给一棵m个结点的无根树,你可以选择一个度数大于1的结点作为根,然后给一些结点(根、内部结点和叶子均可)着以黑色或白色。你的着色方案应该保证根结点到每个叶子的简单路径上都至少包含一个有色结点(哪怕是这个叶子本身)。 

对于每个叶结点u,定义c[u]为从根结点从U的简单路径上最后一个有色结点的颜色。给出每个c[u]的值,设计着色方案,使得着色结点的个数尽量少。

输入描述:

第一行包含两个正整数m, n,其中n是叶子的个数,m是结点总数。结点编号为1,2,…,m,其中编号1,2,… ,n是叶子。
以下n行每行一个0或1的整数(0表示黑色,1表示白色),依次为c[1],c[2],…,c[n]。
以下m-1行每行两个整数a,b(1 ≤ a < b ≤ m),表示结点a和b 有边相连。

输出描述:

仅一个数,即着色结点数的最小值。

示例1

输入

复制

5 3
0
1
0
1 4
2 5
4 5
3 5

输出

复制

2

思路:

假如原来以x为根,y与x相邻,那么x与y的颜色不可能相同。既然颜色不同,那么将根从x变成y对答案显然也不会产生影响。

所以随便选一个点为根,然后树形DP。用dp[x][0/1]表示x的子树中,最后一个点想要得到一个白色/黑色的祖先的最小代价,转移十分容易。

AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<map>
#include<set>
#include<stack>
#include<queue>
#include<vector>
using namespace std;
typedef long long ll;
int n,m;
const int INF=1e4+5;
int c[INF]; 
int head[INF],cnt;
int dp[INF][2];
struct node{
	int di;
	int next;
}f[2*INF];
void init()
{
	int i;
	for(i=0;i<=n;i++)
	{
		head[i]=-1;
	}
	cnt=0;
}
void add(int x,int y)
{
	f[cnt].di =y;
	f[cnt].next =head[x];
	head[x]=cnt++;
}
void dfs(int x,int fa)
{
	dp[x][0]=1;
	dp[x][1]=1;
	if(c[x]==0)
	{
		dp[x][1]=INF;
	}
	if(c[x]==1)
	{
		dp[x][0]=INF;
	}
	int i,j,k,val;
	for(i=head[x];i!=-1;i=f[i].next )
	{
		j=f[i].di ;
		if(j==fa)continue;
		dfs(j,x);
		dp[x][0]+=min(dp[j][0]-1,dp[j][1]);//状态转移方程 
		dp[x][1]+=min(dp[j][1]-1,dp[j][0]);
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	int i,j,k,val;
	memset(c,-1,sizeof(c));
	for(i=1,j=0,k=0;i<=m;i++)
	{
		scanf("%d",&c[i]);
	}
	init();
	for(i=1;i<n;i++)
	{
		scanf("%d%d",&j,&k);
		add(j,k);add(k,j);
	}
	dfs(n,-1);
	printf("%d",min(dp[n][0],dp[n][1]));
    return 0;
}

 

 

2.题目链接:https://ac.nowcoder.com/acm/problem/15748

思路:树形dp,最大独立集。

#include<iostream>//注意所选留宿的城市不一定紧挨着已经游览过的城市 
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<map>
#include<set>
#include<stack>
#include<queue>
#include<vector>
using namespace std;
typedef long long ll;
int n,s,ans; 
const int INF=5e5+5;
vector<int>v[INF];
int vis[INF];
int dp[INF][2];
void dfs(int x)
{
	vis[x]=1;
	int cnt;
	dp[x][1]=1;
	cnt=v[x].size();
	int i,j;
	for(i=0;i<cnt;i++)
	{
		j=v[x][i];
		if(vis[j]==1)continue;
		dfs(j);
		dp[x][1]+=dp[j][0];
		dp[x][0]+=max(dp[j][1],dp[j][0]);
	}
}
int main()
{
	scanf("%d%d",&n,&s);
	int i,j,k;
	for(i=1;i<n;i++)
	{
		scanf("%d%d",&j,&k);
		v[j].push_back(k);
		v[k].push_back(j);	
	}
	ans=0;
    memset(vis,0,sizeof(vis));
    memset(dp,0,sizeof(dp));
    dfs(s);
    printf("%d",dp[s][1]);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值