• 【笔记】【树形dp】


    1>树的直径

    【树形dp】

    (图来自luogu)

     从图中可以得知,以任意一个点为根,树的直径必然有一个dep最小的点,

    直径是这个点子树中,两个最长链的和,(如果只有一个链,另一个当做0就好)

    这里可以用贪心证得

    没有了后效性,就可以 叶子->根 ,dp转移状态了

    #include<cstdio>
    #include<cstdlib>
    #include<vector>
    using namespace std;
    int n,ans;
    const int N=10003;
    vector <int> g[N];
    int d1[N],d2[N];
    
    void dfs(int rt,int f)
    {
        int sz=g[rt].size() ;
        for(int i=0;i<sz;i++)
        {
            int nx=g[rt][i];
            if(nx==f) continue;
            
            dfs(nx,rt);
            if(d1[nx]+1>d1[rt])
            {
                d2[rt]=d1[rt];
                d1[rt]=d1[nx]+1;
            }
            else if(d1[nx]+1>d2[rt])
                d2[rt]=d1[nx]+1;
            ans=max(d1[rt]+d2[rt],ans);
        }
    }
    
    int main()
    {
        int u,v;
        scanf("%d",&n);
        for(int i=1;i<n;i++)
            scanf("%d%d",&u,&v),g[u].push_back(v),g[v].push_back(u);
        
        dfs(1,0);
        printf("%d
    ",ans);
        
        return 0;
    }

    【dfs x 2】

    (来自luogu)

    做两遍dfs,第一遍从任意一个点开始,找到离它最远的点。第二遍从第一遍找到的点开始,再找离他最远的点,这条路径就是树的直径。

    证明: 

    设A和B是直径的两个端点,C是第一次dfsdfs的起点,D是终点。若D不是A或B,则一定有一条更长的链。但这和上面说的“设A和B是直径的两个端点”是矛盾的。

    #include<bits/stdc++.h>
    using namespace std;
    int n,ll,ss=1,ans;
    vector<int>g[10005];//动态数组,不会的看底下的链接
    bool f[100005];//记录该节点是否来过
    inline void dfs(int l,int s,bool flag)//flag只是为了不写两个函数,因为两次dfs都是大同小异
    {
        bool hh=false;//hh用来判断该节点能否向外延伸,如果不能就是搜到了终点
        for(register int i=0;i<g[s].size();i++)
         if(!f[g[s][i]])//如果没有来过
         {
            hh=true;//表示可以向外延伸
            f[g[s][i]]=true;//标记为走过
            dfs(l+1,g[s][i],flag);//继续dfs
         }
        if(!hh)//如果不能延伸
        {
            if(flag) ans=max(ans,l);//如果是要求直径长度,即第二次dfs
            else if(l>ll) ll=l,ss=s;//第一次要记录长度,用来判断,记录端点,给第二次dfs做起点
        }
        return ;
    }
    int main()
    {
        scanf("%d",&n);
        for(register int i=1;i<n;i++)
        {
            int x,y;
            cin>>x>>y;
            g[x].push_back(y);
            g[y].push_back(x);
        }
        dfs(0,1,0);//第一次dfs
        memset(f,0,sizeof(f));//记得初始化!!!
        dfs(0,ss,1);//第二次dfs
        cout<<ans<<'
    ';
        return 0;
    }

    习题:

    数字转换

    #include<cstdio>
    #include<cstdlib>
    #include<queue>
    using namespace std;
    int n;
    const int N=5e4+3;
    int sum[N];
    vector <int> g[N];
    
    void prepare()
    {
        for(int i=1;i<n;i++)
            for(int j=i<<1;j<=n;j+=i)
                sum[j]+=i;
        for(int i=1;i<=n;i++)
            if(sum[i]<i)
                g[i].push_back(sum[i]),
                g[sum[i]].push_back(i);  
    }
    
    int ans;
    int d1[N],d2[N];
    void dfs(int rt,int f)
    {
        int sz=g[rt].size() ;
        for(int i=0;i<sz;i++)
        {
            int nx=g[rt][i];
            if(nx==f) continue;
            dfs(nx,rt);
            
            if(d1[nx]+1>d1[rt])
                d2[rt]=d1[rt],d1[rt]=d1[nx]+1;
            else if(d1[nx]+1>d2[rt])
                d2[rt]=d1[nx]+1;
            ans=max(ans,d1[rt]+d2[rt]);
        }
    }
    
    int main()
    {
        scanf("%d",&n);
        prepare();
        
        dfs(1,0);
        printf("%d
    ",ans);
        
        return 0;
    }

    https://www.cnblogs.com/ymzjj/p/9767526.html

    2>树上动态规划

    例题:皇宫看守

    一个点,只要父亲或者孩子节点被选即可,

    求最小代价

    仿照舞会设计状态,

    找出有影响的状态,这里是分成三种:

    (1)rt没选,fa选了,son不知道              《=       son的状态为2、3

    (2)rt没选,fa不知道,son选了         《=     son的状态为3(一个为3,其他为2、3)

    (3)rt选了,son可选可不选,fa可选可不选    《=     son的状态为1,、2、3

    就像原来是2,3两个状态,现在如果选了fa,rt和son都不用选,所以多了一个状态

    但是因为统计的时候会复杂,所以虽然状态中要求选fa,但是我们统计的时候,值是不包括fa的

    #include<cstdio>
    #include<cstdlib>
    #include<queue>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    int n;
    const int N=1503,inf=1<<30;
    int val[N],sz[N],f[N][3];
    vector <int > g[N];
    
    int root;
    void dfs(int rt)
    {
        f[rt][2]=val[rt];
        int mn=inf;
        
        for(int i=0;i<sz[rt];i++)
        {
            int nx=g[rt][i];
            dfs(nx);
            
            f[rt][0]+=min(f[nx][1],f[nx][2]);
            f[rt][1]+=min(f[nx][1],f[nx][2]);
            mn=min(mn,max(f[nx][2]-f[nx][1],0) );
            f[rt][2]+=min(f[nx][0],min(f[nx][1],f[nx][2]));
        }
        f[rt][1]+=mn;
    }
    
    int main()
    {
        scanf("%d",&n);
        int u,v,w;
        for(int i=1;i<=n;i++)
        {    
            scanf("%d",&u);
            if(!root) root=u;
            scanf("%d%d",&val[u],&sz[u]);
            for(int j=0;j<sz[u];j++)
                scanf("%d",&v),g[u].push_back(v);  
        }
        
        dfs(root);
        printf("%d
    ",min(f[root][1],f[root][2]));
        
        return 0;
    }

    3>树上背包dp

    就跟背包一样做就好了

    #include<cstdio>
    #include<cstdlib>
    #include<vector>
    using namespace std;
    int n,m;
    const int N=303;
    int d[N];
    int f[N][N];
    vector <int> g[N];
    
    void dfs(int rt,int sum)
    {
        if(!sum) return ;
        if(f[rt][sum]) return ;
        for(int i=1;i<=sum;i++)
            f[rt][i]+=d[rt];
        
        int p=sum-1;
        int sz=g[rt].size() ;
        for(int i=0;i<sz;i++)
        {
            int nx=g[rt][i];
            dfs(nx,p);
            
            for(int k=sum;k>1;k--)
                for(int j=1;j<k;j++)
                    f[rt][k]=max(f[rt][k],f[rt][k-j]+f[nx][j]);
        }
    }
    
    int main()
    {
        scanf("%d%d",&n,&m);
        int fa;
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d",&fa,&d[i]);
            g[fa].push_back(i); 
        }
        
        dfs(0,m+1);
        printf("%d
    ",f[0][m+1]);
        
        return 0;
    }
  • 相关阅读:
    where T: class的解释
    调用钉钉的WebAPI接口实现与ERP数据的同步
    Json序列化和反序列化的方式
    Log4Net日志处理
    MVC项目中异常处理
    FindBI商业智能报表工具
    权限列表实现
    委托,匿名,lambda
    [经典贪心算法]贪心算法概述
    [zt]手把手教你写对拍程序(PASCAL)
  • 原文地址:https://www.cnblogs.com/xwww666666/p/11657063.html
Copyright © 2020-2023  润新知