【DP】树形DP题目总结


最近在研究树形DP,所以做了几道比较典型的树形DP的题目。大多数都是一些比较经典的题,大部分都用了多叉转二叉,题目也比较类似。感觉树形DP的模式都比较固定,基本都是先建树,然后dfs,方程也基本都是枚举左子树和右子树各自分配资源得到根的最大值,具体见题目吧。

 1、rqnoj30:愚蠢的矿工

#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 1000 + 10;
const int maxm = 100 + 10;
struct tree
{
	int l,r;
	tree()
	{
		l = r = -1;
	}
}Tree[maxn];
int n,m;
int w[maxn];
int f[maxn][maxm];
bool map[maxn][maxn];
void init()
{
	freopen("rqnoj30.in","r",stdin);
	freopen("rqnoj30.out","w",stdout);
}

void readdata()
{
	memset(map,false,sizeof(map));
	scanf("%d%d",&n,&m);
	for(int i = 1;i <= n;i++)
	{
		scanf("%d",&w[i]);
	}
	for(int i = 1;i <= n;i++)
	{
		int a,b;
		scanf("%d%d",&a,&b);
		map[a][b] = true;
	}
}

void maketree(int k)
{
	for(int i = 1;i <= n;i++)
	{
		if(map[k][i])
		{
			if(Tree[k].l == -1)
			{
				Tree[k].l = i;
			}
			else
			{
				int t = Tree[k].l;
				while(Tree[t].r != -1)t = Tree[t].r;
				Tree[t].r = i;
			}
		}
	}
	if(Tree[k].l != -1)maketree(Tree[k].l);
	if(Tree[k].r != -1)maketree(Tree[k].r);
}

int max(int a,int b)
{
	return a > b ? a : b;
}

int dfs(int d,int k)
{
	if(d == -1)return 0;
	if(f[d][k] != -1)return f[d][k];
	f[d][k] = dfs(Tree[d].r,k);
	for(int i = 0;i <= k - 1;i++)
	{
		f[d][k] = max(dfs(Tree[d].l,i) + dfs(Tree[d].r,k-i-1) + w[d],f[d][k]);
	}
	return f[d][k];
}

void solve()
{
	memset(f,-1,sizeof(f));
	for(int i = 1;i <= n;i++)f[i][0] = 0;
	maketree(0);
	printf("%d",dfs(0,m+1));
}

int main()
{
	init();
	readdata();
	solve();
	return 0;
}



 2、ural1018:二叉苹果树

#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 100 + 10;
struct tnode
{
	int l,r;
}tree[maxn];
int map[maxn][maxn];
int w[maxn];
int f[maxn][maxn];
int n,q;
void init()
{
	freopen("ural1018.in","r",stdin);
	freopen("ural1018.out","w",stdout);
}

void readdata()
{
	memset(map,-1,sizeof(map));
	scanf("%d%d",&n,&q);
	for(int i = 1;i < n;i++)
	{
		int l,r,w;
		scanf("%d%d%d",&l,&r,&w);
		map[l][r] = map[r][l] = w;
	}
}

void maketree(int v)
{
	for(int i = 1;i <= n;i++)
	{
		if(map[v][i] >= 0)
		{
			tree[v].l = i;
			w[i] = map[v][i];
			map[v][i] = map[i][v] = -1;
			maketree(i);
			break;
		}
	}
	for(int i = 1;i <= n;i++)
	{
		if(map[v][i] >= 0)
		{
			tree[v].r = i;
			w[i] = map[v][i];
			map[v][i] = map[i][v] = -1;
			maketree(i);
			break;
		}
	}
}

int max(int a,int b)
{
	return a > b ? a : b;
}

void dfs(int v,int k)
{
	if(k == 0)
	  f[v][k] = 0;
	else if(tree[v].l == 0 && tree[v].r == 0)
		 {
		     f[v][k] = w[v];
		 }
		 else
		 {
			 f[v][k] = 0;
			 for(int i = 0;i < k;i++)
			 {
				 if(f[tree[v].l][i] == 0)dfs(tree[v].l,i);
				 if(f[tree[v].r][k-i-1] == 0)dfs(tree[v].r,k-i-1);
				 f[v][k] = max(f[v][k],f[tree[v].l][i]+f[tree[v].r][k-i-1] + w[v]);
			 }
		 }
}

void solve()
{
	maketree(1);
	dfs(1,q+1);
	int ans = f[1][q+1];
	printf("%d",ans);
}

int main()
{
	init();
	readdata();
	solve();
	return 0;
}


 

 3、tyvj1051:选课

#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 300 + 10;
struct tree
{
	int l,r;
	tree()
	{
		l = r = -1;
	}
}Tree[maxn];
bool map[maxn][maxn];
int num[maxn];
int f[maxn][maxn];
int n,m;
void init()
{
	freopen("tyvj1051.in","r",stdin);
	freopen("tyvj1051.out","w",stdout);
}

void readdata()
{
	memset(map,false,sizeof(map));
	scanf("%d%d",&n,&m);
	for(int i = 1;i <= n;i++)
	{
		int t,w;
		scanf("%d%d",&t,&w);
		map[t][i] = true;
		num[i] = w;
	}
}

void maketree(int k)
{
	for(int i = 1;i <= n;i++)
	{
		if(map[k][i])
		{
			if(Tree[k].l == -1)
			{
				Tree[k].l = i;
			}
			else
			{
				int t = Tree[k].l;
				while(Tree[t].r != -1)t = Tree[t].r;
				Tree[t].r = i;
			}
		}
	}
	if(Tree[k].l != -1)maketree(Tree[k].l);
	if(Tree[k].r != -1)maketree(Tree[k].r);
}

int max(int a,int b)
{
	return a > b ? a : b;
}

int dfs(int t,int m)
{
	if(t == -1)return 0;
	if(f[t][m] != -1)return f[t][m];
	f[t][m] = dfs(Tree[t].r,m);
	for(int i = 0;i <= m - 1;i++)
	{
		f[t][m] = max(dfs(Tree[t].l,i) + dfs(Tree[t].r,m-i-1) + num[t],f[t][m]);
	}
	return f[t][m];
}


void solve()
{
	memset(f,-1,sizeof(f));
	for(int i = 1;i <= n;i++)f[i][0] = 0;
	maketree(0);
	printf("%d",dfs(0,m+1));
}

int main()
{
	init();
	readdata();
	solve();
	return 0;
}


 

 4、tyvj1052:没有上司的舞会

#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 6000 + 10;
struct tree
{
	int l,r;
	tree()
	{
		l = r = -1;
	}
}Tree[maxn];
int root,n;
int f[maxn][2];
int w[maxn];
bool map[maxn][maxn];
bool flag[maxn];
void init()
{
	freopen("tyvj1052.in","r",stdin);
	freopen("tyvj1052.out","w",stdout);
}

void readdata()
{
	memset(flag,true,sizeof(flag));
	memset(map,false,sizeof(map));
	scanf("%d",&n);
	for(int i = 1;i <= n;i++)
	{
		scanf("%d",&w[i]);
	}
	for(int i = 1;i < n;i++)
	{
		int l,k;
		scanf("%d%d",&l,&k);
		map[k][l] = true;
		flag[l] = false;
	}
	for(int i = 1;i <= n;i++)
	{
		if(flag[i])
		{
			root = i;
			break;
		}
	}
}

void maketree(int k)
{
	for(int i = 1;i <= n;i++)
	{
		if(map[k][i])
		{
			if(Tree[k].l == -1)
			{
				Tree[k].l = i;
			}
			else
			{
				int t = Tree[k].l;
				while(Tree[t].r != -1)t = Tree[t].r;
				Tree[t].r = i;
			}
		}
	}
	if(Tree[k].l != -1)maketree(Tree[k].l);
	if(Tree[k].r != -1)maketree(Tree[k].r);
}

int max(int a,int b)
{
	return a > b ? a : b;
}

int dfs(int k,int s)
{
	if(f[k][s] != -1)return f[k][s];
	f[k][s] = 0;
	if(s == 1)f[k][s] = w[k];
	for(int i = Tree[k].l;i != -1;i = Tree[i].r)
	{
		int com;
		if(s == 0)
		{
			com = max(dfs(i,0),dfs(i,1));
			if(com >= 0)f[k][s] += com;
		}
		if(s == 1)
		{
			com = dfs(i,0);
			if(com >= 0)f[k][s] += com;
		}
	}
	return f[k][s];
}

void solve()
{
	memset(f,-1,sizeof(f));
	maketree(root);
	int ans = max(dfs(root,0),dfs(root,1));
	printf("%d",ans);
}

int main()
{
	init();
	readdata();
	solve();
	return 0;
}


 

 5、tyvj1513:小胖守皇宫

 这道题题意有点不好理解,每个节点有三种状态:第一种是在该节点放士兵;第二种是在该节点不放,但在它的儿子节点放了士兵;第三种是该节点不放,它被父亲节点影响。所以第一种情况可以由三种情况转移,第二种情况能由1,2种情况转移,第三种情况只能由第二种情况来转移。

#include<cstdio>
#include<cstring>
using namespace std;
const int inf = 0x3ffff;
const int maxn = 1500 + 10;
const int maxm = 5;
struct tree
{
	int l,r;
	tree()
	{
		l = r = -1;
	}
}Tree[maxn];
int w[maxn];
int f[maxn][maxm];
int n,root;
bool vis[maxn][maxm];
bool map[maxn][maxn];
bool flag[maxn];
void init()
{
	freopen("tyvj1513.in","r",stdin);
	freopen("tyvj1513.out","w",stdout);
}

void readdata()
{
	memset(flag,false,sizeof(flag));
	memset(map,false,sizeof(map));
	scanf("%d",&n);
	for(int i = 1;i <= n;i++)
	{
		int t1,m;
		scanf("%d",&t1);
		scanf("%d",&w[t1]);
		scanf("%d",&m);
		for(int j = 1;j <= m;j++)
		{
			int t2;
			scanf("%d",&t2);
			map[t1][t2] = true;
			flag[t2] = true;
		}
	}
	for(int i = 1;i <= n;i++)
	{
		if(flag[i] == false)
		{
			root = i;
			break;
		}
	}
}

void maketree(int k)
{
	for(int i = 1;i <= n;i++)
	{
		if(map[k][i])
		{
			if(Tree[k].l == -1)
			{
				Tree[k].l = i;
			}
			else
			{
				int t = Tree[k].l;
				while(Tree[t].r != -1)t = Tree[t].r;
				Tree[t].r = i;
			}
		}
	}
	if(Tree[k].l != -1)maketree(Tree[k].l);
	if(Tree[k].r != -1)maketree(Tree[k].r);
}

int min(int a,int b)
{
	return a < b ? a : b;
}

int dfs(int k,int s)
{
	if(vis[k][s])return f[k][s];
	vis[k][s] = true;
	int sum = 0,temp = inf;
	for(int i = Tree[k].l;i != -1;i = Tree[i].r)
	{
		if(s == 1)
		{
			f[k][s] += min(dfs(i,1),min(dfs(i,2),dfs(i,3)));
		}
		if(s == 2)
		{
			f[k][s] += min(dfs(i,1),dfs(i,2));
			if(f[i][1] - min(f[i][1],f[i][2]) < temp)
			{
				temp = f[i][1] - min(f[i][1],f[i][2]);
			}
		}
		if(s == 3)
		{
			f[k][s] += dfs(i,2);
		}
	}
	if(s == 1)f[k][s] += w[k];
	if(s == 2)f[k][s] += temp;
	return f[k][s];
}

void solve()
{
	memset(vis,false,sizeof(vis));
	memset(f,0,sizeof(f));
	maketree(root);
	int ans = min(dfs(root,1),dfs(root,2));
	printf("%d",ans);
}

int main()
{
	init();
	readdata();
	solve();
	return 0;
}

The End...

你可能感兴趣的:(【DP】树形DP题目总结)