树形dp大体结构
void dfs(int u,int fa)
{
是否为叶子结点
如果是的话,就执行一些底层的信息的记录
如果不是
遍历结点u所有连接的边,并取出对应的端点
如果端点是父亲结点
continue;
}
参数列表里面不单含有当前这个结点,还包括当前这个结点的父亲结点,这是为了避免在遍历u所连接的结点的时候出现冲突。
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。