听说考前1天发题解会rp++哦
题目链接;
1.解题意
给你一棵树,求树上最大的“毛毛虫”
其中,毛毛虫的定义为:一条主链+主链所包含的所有节点所连的一条边。
2.找思路
容易看出是树上dp。
树上dp最难的环节是思考dp方程。
从叶子节点开始考虑:
对于1个节点,很明显答案是1。
往上一个节点,如果上面的节点不是分叉节点,此时毛毛虫的形状还是条链。答案+1
接着扩大情况,此时,我们遇到了分叉节点。这个节点除了当前的子树之外还有其他分支,考虑分支的合并。
当考虑到当前有分支的节点合并的问题时,我们必定已经遍历完了子树节点,自然清楚各个子树的最大毛毛虫;于是我们可以去max,加上当前节点的大小1,而其他的子树可以作为毛毛虫的“毛”,每一个其他分支可以对答案造成1的贡献。
综合起来,我们设f[v]为包含v节点的v节点及其子树的最大毛毛虫方案。
f[now]=max(f[v])+1+size[now]-2;其中,v是now节点的子节点,size[now]为当前节点连的边的个数,-2就是减去连向父节点的边和最大的f[v]所在的边。
可能有人觉得此时dp方程已经搞完了吗,只差代码实现。但是我们忽略的一个问题:毛毛虫可能盘曲在子树中;
——来源于洛谷题解
这种情况下,毛毛虫就只能是由:最大的f[v],次大的f[v],size-3得到。
我们还要统计一个次大值才行。
但是由于转移方程的定义,f[]数组不能表示这种情况,只有表示直着的才能转移。
那么我们只需要在每个结点时统计一下答案即可。
ans=mx0+mx1+1+max(0,size[now]-2);
其中,mx0为最大值,mx1为次大值。
代码:
#include<iostream> #include<cstdio> #include<cstring> #define N 300007 using namespace std; inline int read() { int ans=0; char ch=getchar(),last=' '; while(ch>'9'||ch<'0')last=ch,ch=getchar(); while(ch>='0'&&ch<='9')ans=(ans<<1)+(ans<<3)+ch-'0',ch=getchar(); return last=='-'?-ans:ans; } int n,a,ans,hea[N],num,siz[N],u,v,m; int f[N]; struct edg{ int nex,to; }edge[N<<1]; inline void add(int from,int to) { num++; edge[num].nex=hea[from]; edge[num].to=to; hea[from]=num; } int dfs(int now,int fa) { int mx1=0,mx0=0; for(int i=hea[now];i;i=edge[i].nex) { int v=edge[i].to; if(v==fa)continue; dfs(v,now); f[now]=max(f[now],f[v]); if(f[v]>mx0)mx1=mx0,mx0=f[v]; else if(f[v]>mx1)mx1=f[v]; } f[now]+=1+max(0,siz[now]-2); ans=max(ans,mx0+mx1+1+max(0,siz[now]-2)); } int main(){ n=read();m=read(); for(int i=1;i<=m;i++) { u=read(),v=read(); add(u,v);siz[u]++; add(v,u);siz[v]++; } dfs(1,0); printf("%d ",ans); return 0; }
完结撒花
csp2020 rp++。