• 树形dp


    例1:没有上司的舞会https://www.luogu.com.cn/problem/P1352

    明显的树形结构

    三种状态

    1、上司去,而剩下的这个上司的职员就不会去。
    2、不去,而那个职员去
    3、上司不去,而那个职员也不去。

    核心代码(f[v][0/1]是已经迭代出来的了)

    f[u][0]+=maxx(f[v][1],f[v][0]);
    f[u][1]+=f[v][0];
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<vector>
    #include<algorithm>
    using namespace std;
    #define N 6005
    int f[N][2],st,w[N];
    int n,m;
    int maxx(int x,int y){
        return x>y?x:y;
    }
    vector<int>g[N];
    bool vis[N];
    void dfs(int u,int fa){
        f[u][0]=0;f[u][1]=w[u];
        int siz=g[u].size();
        for(int i=0;i<siz;i++){
            int v=g[u][i];
            if(v==fa)continue;
            dfs(v,u);
            f[u][0]+=maxx(f[v][1],f[v][0]);
            f[u][1]+=f[v][0];
        }
    }
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d",&w[i]);
        for(int i=1,u,v;i<=n;i++){
            scanf("%d%d",&u,&v);
            if(u==0)continue;
            vis[u]=1;
            g[v].push_back(u);
        }
        for(int i=1;i<=n;i++){
            if(!vis[i]){
                st=i;dfs(st,0);break;
            }
        }
        
        printf("%d
    ",maxx(f[st][0],f[st][1]));
        return 0;
    }
    1

    类似于有依赖的背包

    f(i)(j)表示在以 i 为根的子树中,选择 j 个点并且一定选择 i 的最大价值。

    这里以0节点为开始的根

    把选课前需要学的课与其连边(若没有要学的和0连边)

    很明显,f(i)(1)就是其自己学分

    然后转移类似01背包(倒序)

    for(int j=m+1;j>=1;j--)
        for(int k=0;k<j;k++)
            f[x][j]=maxx(f[x][j],f[y][k]+f[x][j-k]);
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    
    #define N 1005
    using namespace std;
    int n,m,f[N][N];
    vector<int>g[N];
    int maxx(int x,int y){
        return x>y?x:y;
    }
    void dfs(int x){
        int siz=g[x].size();
        for(int i=0;i<siz;i++){
            int y=g[x][i];dfs(y);
            for(int j=m+1;j>=1;j--)
                for(int k=0;k<j;k++)
                    f[x][j]=maxx(f[x][j],f[y][k]+f[x][j-k]);
        }
    }
    int main(){
        scanf("%d%d",&n,&m) ;
        for(int i=1,x;i<=n;i++){
            scanf("%d",&x);
            scanf("%d",&f[i][1]);
            g[x].push_back(i);
        }
        dfs(0);
        printf("%d
    ",f[0][m+1]);
        return 0;
    }
    选课

     例3:二叉苹果树https://www.luogu.com.cn/problem/P2015

    一棵二叉树,边上有苹果,给定需要保留的边数量,求出最多能留住多少苹果。

    维护子树大小siz[ ]

    为什么是f[u][i-j-1]而不是f[u][i-j]

    为前文提到了,保留一条边必须保留从根节点到这条边路径上的所有边,那么如果你想从u的子节点v的子树上留边的话,也要留下u,v之间的连边

    那么取值范围k为什么要小于等于i-1而不是i呢?
    同上,因为要保留u,v连边

    对了,别忘了i,j要倒序枚举因为这是01背包
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<vector>
    #include<utility>
    using namespace std;
    #define N 105
    inline int read(){
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
        while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    int f[N][N],siz[N];
    int n,m;
    vector< pair<int,int> >g[N];
    void dfs(int x,int fa){
        for(int i=0;i<g[x].size();i++){
            int y=g[x][i].first;
            if(y==fa)continue;
            dfs(y,x);
            siz[x]+=siz[y]+1;
            for(int j=min(siz[x],m);j>0;j--)
                for(int k=min(j-1,siz[y]);k>=0;k--)
                    f[x][j]=max(f[x][j],f[x][j-k-1]+f[y][k]+g[x][i].second);
            }
        
    }
    int main(){
        n=read();m=read();
        for(int i=1,x,y,z;i<n;i++){
            x=read();y=read();z=read();
            g[x].push_back(make_pair(y,z));
            g[y].push_back(make_pair(x,z));
        }
        dfs(1,-1);
        printf("%d
    ",f[1][m]);
        return 0;
    }
    View Code
     
    例4:最大子树和 https://www.luogu.com.cn/problem/P1122
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<vector>
    using namespace std;
    #define N 16050
    inline int read(){
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
        while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    int f[N];
    int n,ans;
    vector< int >g[N];
    void dfs(int x,int fa){
        for(int i=0;i<g[x].size();i++){
            int y=g[x][i];
            if(y==fa)continue;
            dfs(y,x);
            f[x]+=max(f[y],0);
        }
        ans=max(ans,f[x]);
    }
    int main(){
        n=read();
        for(int i=1;i<=n;i++)f[i]=read();
        for(int i=1,x,y;i<n;i++){
            x=read();y=read();
            g[x].push_back(y);
            g[y].push_back(x);
        }
        dfs(1,0);
        printf("%d
    ",ans);
        return 0;
    }
    4
    例5:战略游戏 https://www.luogu.com.cn/problem/P2016
    #include<iostream>
    #include<cstdio>
    #include<vector>
    using namespace std;
    inline int read(){
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
        while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    const int N = 2000;
    int n,ans,f[N][2];
    vector< int >g[N]; 
    bool vis[N];
    void dfs(int x,int fa){
        f[x][1]=1;f[x][0]=0;
        for(int i=0;i<g[x].size();i++){
            int y=g[x][i];
            if(y==fa)continue;
            dfs(y,x);
            f[x][0]+=f[y][1];
            f[x][1]+=min(f[y][1],f[y][0]);
        }
    }
    int main(){
        n=read();
        for(int i=1,x,y,num;i<=n;i++){
            x=read();num=read();
            while(num--){
                y=read();
                g[x].push_back(y);
                g[y].push_back(x);
            }
        } 
        dfs(0,-1); 
        printf("%d",min(f[0][0],f[0][1]));
        return 0;
    }
     
  • 相关阅读:
    day1记一次无列名注入
    无参数RCE
    ThinkPHP 5.x远程命令执行漏洞
    phpmyadmin4.8.1文件包含漏洞
    foreach循环导致变量覆盖
    绕过空格的报错注入
    布尔盲注payload补充
    php后台验证两种方式绕过
    CentOS yum 配置阿里镜像
    CentOS通过yum安装配置Java环境
  • 原文地址:https://www.cnblogs.com/liukx/p/12676257.html
Copyright © 2020-2023  润新知