【算法笔记】树形dp

·前言

树形dp,顾名思义,就是在树上进行的dp,它一般会搭配上深度优先搜索进行,以记录下所以的孩子/父亲的状态用于转移
(可能会出现一大堆的计数题,感觉这种dp特别适合用来整计数)

·基本思维

树形dp最大的特点就是在一边搜索的时候一边记录答案,这样子的话调用答案的时候会比较方便。而且,当遇到图论题的时候,其实也可以把图转化成一棵树来解决问题。
比较简单的树形dp思路相对好想,码量也不算大(但是难打的树形dp思路就会特别毒瘤,根本就想不出来啊……

这里给出一些树形dp比较常用的板子:
·树的最大独立集
#include<bits/stdc++.h>
using namespace std;
int n,f[100010][25],g[100010],s[100010],d[100010];
vector<int>v[100010];
void dfs(int x,int fa){
    for(int i=1;i<=20;i++)f[x][i]=f[f[x][i-1]][i-1];
    for(int i=0;i<v[x].size();i++){
        int to=v[x][i];
        if(to==fa)continue;
        f[to][0]=x;
        dfs(to,x);
    }
    d[x]=max(s[x],g[x]+1);
    s[f[x][0]]+=d[x];
    g[f[x][1]]+=d[x];
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<n;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        v[x].push_back(y);
        v[y].push_back(x);
    }
    dfs(1,0);
    printf("%d",d[1]);
    return 0;
}
View Code

·树的重心

#include<bits/stdc++.h>
using namespace std;
int son[100010],vis[100010],n,size,ans;
vector<int>v[100010];
void dfs(int x){
    vis[x]=1;son[x]=0;
    int maxn=0;
    for(int i=0;i<v[x].size();i++){
        int to=v[x][i];
        if(!vis[to]){
            dfs(to);
            son[x]+=son[to]+1;
            maxn=max(maxn,son[to]+1);
        }
    }
    maxn=max(maxn,n-son[x]-1);
    if(maxn<size||maxn==size&&x<ans)ans=x,size=maxn;
}
int main(){
    scanf("%d",&n);
    size=1e9;
    for(int i=1;i<n;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        v[x].push_back(y);
        v[y].push_back(x);
    }
    dfs(1);
    printf("%d %d\n",ans,size);
    return 0;

}
View Code

·树上最长路径

#include<bits/stdc++.h>
using namespace std;
int n,vis[200010],d[200010];
vector<int>v[200010]; 
int spfa(int u){
    queue<int>q;
    memset(d,0x3f,sizeof(d));
    memset(vis,0,sizeof(vis));
    q.push(u);
    d[u]=0;vis[u]=1;
    while(!q.empty()){
        int x=q.front();
        q.pop();
        for(int i=0;i<v[x].size();i++){
            int to=v[x][i];
            if(d[to]>d[x]+1){
                d[to]=d[x]+1;
                if(!vis[to]){
                    vis[to]=1;
                    q.push(to);
                }
            }
        }
        vis[x]=0;
    }
    int maxn=0,id;
    for(int i=1;i<=n;i++)
        if(i!=u){
            if(d[i]>maxn)id=i,maxn=d[i];
        }
    return id;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<n;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        v[x].push_back(y);
        v[y].push_back(x);
    }
    int ans=0,id=spfa(1);
    ans+=d[id];
    int ans1=ans,id2=spfa(id);
    if(id==1)ans=max(ans,d[id2]);
    else ans+=d[id2];
    printf("%d\n",ans-ans1);
    return 0;
}
View Code

·题目汇总

poj2342/hdu1520 Anniversary party
hdu6201 transaction transaction transaction
hdu2196 Computer
Luogu2014 CTSC1997
hdu6035 Colorful Tree
codeforces 1101D/990G Gcd Counting
Luogu3177 HAOI2015
UVA10859 Placing Lampposts
*先不放链接啦,先把题目放在这里,以后会把代码补上。
posted @ 2020-08-07 22:50  linsky  阅读(148)  评论(1编辑  收藏  举报