树形DP

树形dp大体结构

void dfs(int u,int fa)
{
     是否为叶子结点
        如果是的话,就执行一些底层的信息的记录
     如果不是
        遍历结点u所有连接的边,并取出对应的端点
        如果端点是父亲结点
           continue;
}

参数列表里面不单含有当前这个结点,还包括当前这个结点的父亲结点,这是为了避免在遍历u所连接的结点的时候出现冲突。

【CF】【 树形DP】F. Zublicanes and Mumocrates_结点

f[i][j][0/1]代表的是第i结点被染上颜色0/1并且具有j个叶子结点被染上颜色1的存在两个端点异色的边的最少边数

题目要求的是,当叶子结点对半染不同的颜色,所以最终要求的是整棵树内最少要求的边数。

min(f[root][total/2][0],f[root][total/2][1])

total记录的是叶子结点的个数。

#include <bits/stdc++.h>
#define MEM(a,x) memset(a,x,sizeof(a))
#define W(a) while(a)
#define gcd(a,b) __gcd(a,b)
#define pi acos(-1.0)
#define PII pair<int,int>
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define ll long long
#define ull unsigned long long
#define rep(i,x,n) for(int i=x;i<n;i++)
#define repd(i,x,n) for(int i=x;i<=n;i++)
#define MAX 1000005
#define MOD 1000000007
#define INF 0x3f3f3f3f
#define lowbit(x) (x&-x)
using namespace std;
const int N = 5E3+10,M=1E4+100;
int h[N],ne[M],e[M],idx;
void add(int a,int b)
{
	e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
int f[N][N][2],g[N][2],sons[N],n,m,du[N];
void dfs(int u,int fa)
{
	if(du[u]==1)
	{
		sons[u]=1;
	    f[u][1][1]=0,f[u][0][0]=0;
		return ;
	}
	f[u][0][0]=0,f[u][0][1]=0;//此时u结点没有连上任何结点,相当于是一种断联的状态 
	for(int i=h[u];~i;i=ne[i])
	{
		int j=e[i];
		if(j==fa) continue;
		
		dfs(j,u);
		memset(g,0x3f,sizeof(g));
		for(int k=0;k<=sons[u];k++)//未加入儿子结点j所拥有的状况 
		{
			//由已知量扩展到未知量 
			for(int kk=0;kk<=sons[j];kk++)
			{
				g[k+kk][0]=min(g[k+kk][0],f[u][k][0]+f[j][kk][1]+1);
				g[k+kk][0]=min(g[k+kk][0],f[u][k][0]+f[j][kk][0]);
				g[k+kk][1]=min(g[k+kk][1],f[u][k][1]+f[j][kk][0]+1);
				g[k+kk][1]=min(g[k+kk][1],f[u][k][1]+f[j][kk][1]);
			}
		}
		for(int k=0;k<=sons[u]+sons[j];k++)
		   for(int kk=0;kk<2;kk++)
		       f[u][k+kk][0]=g[k+kk][0],f[u][k+kk][1]=g[k+kk][1];
		sons[u]+=sons[j];
	}
}

int main()
{
	memset(h,-1,sizeof(h));
	memset(f,0x3f,sizeof(f));
	scanf("%d",&n);
	for(int i=1;i<=n-1;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		add(x,y);add(y,x);
		du[x]++,du[y]++;
	}
	int ones=0,root;
	for(int i=1;i<=n;i++)
	    if(du[i]==1) 
	      ones++;
	    else 
	      root=i;
	dfs(root,-1);
	cout<<min(f[root][ones/2][0],f[root][ones/2][1]);
    return 0;
}

其他
  • 在一颗树里面,只要一个结点的度大于等于2,就有资本去作为,一棵树的根节点(可以调换)。

  • 叶子结点度为1

  • C库函数memcpy

    C 库函数 void memcpy(void str1, const void str2, size_t n) 从存储区 str2 复制 n 个字节到存储区 str1

    memcpy(dst,ori+pos,num)

    代表是的是从ori中第pos的位置(包括pos这个位置上的内容),连续赋值num个内容过去给dst.

树状DP的精髓

假设一个结点有n个儿子结点,那么在DP的过程中会先去计算一部分,然后其他的会站在已经计算了的基础上再做DP。