【蓝桥杯集训28】树形DP(4 / 4)

目录

285. 没有上司的舞会 - 以u为根的子树中,01选择u点

1072. 树的最长路径 - 最长路径+次长路径

323. 战略游戏 - 以u为根的子树中,01选择u点

1220. 生命之树


285. 没有上司的舞会 - 以u为根的子树中,01选择u点

活动 - AcWing

题目:

选了某个节点就不能选其父节点和子节点,求最大权值和

思路:

  • f[u][0]为所有以u为根的子树中选择,且不选择u这个节点的最大快乐值
  • f[u][1]为所有以u为根的子树中选择,且选择u这个节点的最大快乐值

找到根节点,向下dfs

遍历与该节点u相邻的节点

  • 不选该节点,则它的子树可以选or不选
  • 如果选该节点,则它的子树都不能选

答案就是max(f[root][0],f[root][1])

import java.util.*;

class Main
{
    static int N=6010;
    static int[] h=new int[N],ne=new int[N],e=new int[N];
    static int idx;
    static int[] w=new int[N];
    static boolean[] st=new boolean[N]; //记录有父节点的点
    static int[][] f=new int[N][2];
    
    public static void add(int a,int b)
    {
        e[idx]=b;ne[idx]=h[a];h[a]=idx++;
    }
    
    public static void dfs(int u)
    {
        f[u][1]=w[u]; //选这个根节点的话
        
        for(int i=h[u];i!=-1;i=ne[i])
        {
            int j=e[i];
            dfs(j);
            f[u][0]+=Math.max(f[j][1],f[j][0]); //如果不选这个根节点 则子树有选or不选两种选择
            f[u][1]+=f[j][0]; //如果选这个根节点 则子树只能不选
        }
    }
    
    public static void main(String[] args)
    {
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        for(int i=1;i<=n;i++) w[i]=sc.nextInt();
        
        Arrays.fill(h,-1);
        
        for(int i=0;i<n-1;i++)
        {
            int a=sc.nextInt(),b=sc.nextInt();
            add(b,a);
            st[a]=true;
        }
        
        int root=1;
        while(st[root]) root++; //找到根节点
        
        dfs(root);
        
        int res=Math.max(f[root][0],f[root][1]);
        System.out.print(res);
    }
}

1072. 树的最长路径 - 最长路径+次长路径

活动 - AcWing

题目:

给定一棵树,树中包含 n 个结点(编号1~n)和 n−1 条无向边,每条边都有一个权值

在树中找到一条路径,使得使得路径两端的点的距离最远

输出树的最长路径长度

思路:

  • f[0][i]是以节点i为根的子树中,从子树的某个节点到i的最长路径
  • f[1][i]是以节点i为根的子树中,从子树的某个节点到i的次长路径

答案就是最大的最长路径和次长路径之和   res=max(f[0][i]+f[1][i])

初始选取任意点作为根节点dfs,这样整颗树的拓扑结构被唯一确定

对于每一个节点u,遍历它的相邻的子节点

  • 如果子节点的最长路+w[i] > 当前最长路,则更新最长路和次长路
  • 否则如果>当前次长路,则更新次长路

答案res更新以u为根节点的最长路径长度

import java.util.*;

class Main
{
    static int N=10010,M=N<<1;
    static int[][] f=new int[2][N];
    static int[] h=new int[N],ne=new int[M],e=new int[M],w=new int[M];
    static int res,idx,n;
    
    public static void add(int a,int b,int c)
    {
        e[idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx++;
    }
    
    public static void dfs(int u,int father)
    {
        for(int i=h[u];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(j==father) continue; //只遍历相邻子节点
            dfs(j,u); //先探子树
            if(f[0][j]+w[i]>f[0][u]) 
            {
                f[1][u]=f[0][u]; //u的次长路=之前的最长路
                f[0][u]=f[0][j]+w[i];
            }
            else if(f[0][j]+w[i]>f[1][u]) f[1][u]=f[0][j]+w[i];
        }
        res=Math.max(res,f[1][u]+f[0][u]);
    }
    
    public static void main(String[] args)
    {
        Scanner sc=new Scanner(System.in);
        Arrays.fill(h,-1);
        n=sc.nextInt();
        for(int i=0;i<n-1;i++)
        {
            int a=sc.nextInt(),b=sc.nextInt(),c=sc.nextInt();
            add(a,b,c); add(b,a,c);
        }
        dfs(1,-1); //任意选取一个点作为根节点,这样整棵树的拓扑结构被唯一确定
        System.out.print(res);
    }
}

323. 战略游戏 - 以u为根的子树中,01选择u点

活动 - AcWing

题目:

给定一棵包含 n 个结点的树,以及树上的 n−1 条边

我们需要在这 n 个结点中,选择一定数量的结点放上哨兵

最终要求,树中任意 n−1 条边的左右两端,至少有一个结点上放置了哨兵

求解一个方案,该方案满足上述要求,且放置的哨兵数量最少,输出该方案放置的哨兵数量

思路:

这题和上面那道【没有上司的舞会】是一个类型

都是选取某个点为根节点的子树,看选不选这个点的xx值

  • f[i][0]以i为根节点的子树 i这个节点不放哨兵的最小士兵数
  • f[i][1]以i为根节点的子树 i这个节点不放哨兵的最小士兵数

  • 如果当前节点u放了哨兵,则子树中连向u的另一端,可以放也可以不放
  • f[u][1]+=max( f[j][1],f[j][0] )
  • 如果当前节点没放哨兵,则子树中连向u的另一端,必须放
  • f[u][0]+=f[j][1]

找到根节点,从根节点向下dfs

答案就是max(f[root][0],f[root][1])

import java.util.*;

class Main
{
    static int N=1600;
    static int[] st=new int[N];
    static int[] h=new int[N],ne=new int[N],e=new int[N];
    static int[][] f=new int[N][2]; 
    //f[i][0]以i为根节点的子树 i这个节点不放哨兵的最小士兵数
    //f[i][1]以i为根节点的子树 i这个节点不放哨兵的最小士兵数
    static int n,idx;
    
    public static void add(int a,int b)
    {
        e[idx]=b;ne[idx]=h[a];h[a]=idx++;
    }
    
    public static void dfs(int u)
    {
        f[u][1]=1; //放哨兵的话
        for(int i=h[u];i!=-1;i=ne[i])
        {
            int j=e[i];
            dfs(j);
            f[u][0]+=f[j][1];
            f[u][1]+=Math.min(f[j][1],f[j][0]);
        }
    }
    
    public static void main(String[] args)
    {
        Scanner sc=new Scanner(System.in);
        while(sc.hasNext())
        {
            Arrays.fill(st,0);
            Arrays.fill(h,-1);
            for(int i=0;i<N;i++) Arrays.fill(f[i],0);
            idx=0;
            n=sc.nextInt();
            for(int i=0;i<n;i++)
            {
                String s=sc.next();
                int id=Integer.parseInt(s.substring(0,s.indexOf(":")));
                int num=Integer.parseInt(s.substring(s.indexOf("(")+1,s.indexOf(")")));
                while(num-->0)
                {
                    int x=sc.nextInt();
                    st[x]=1;
                    add(id,x);
                }
            }
            int root=0;
            while(st[root]==1) root++;
            
            dfs(root);
            
            System.out.println(Math.min(f[root][0],f[root][1]));
        }
    }
}

1220. 生命之树

活动 - AcWing

题目:

求树上连通块的最大和

思路:

f[i]以i为根的子树(包含i)的连通块的最大和

初始选取任意点作为根节点dfs,这样整颗树的拓扑结构被唯一确定

对于每一个节点u,遍历它的相邻的子节点:

每次累加子树的连通块最大和,如果小于0就不加

最后遍历所有节点,看以哪个节点做根连通块和最大

import java.util.*;

class Main
{
    static int N=100010,M=N*2;
    static long[] f=new long[N]; //f[i]以i为根的子树(包含i)的连通块的最大和
    static int[] h=new int[N],ne=new int[M],e=new int[M],w=new int[N];
    static int idx,n;
    
    public static void add(int a,int b)
    {
        e[idx]=b;ne[idx]=h[a];h[a]=idx++;
    }
    
    public static void dfs(int u,int father)
    {
        f[u]=w[u];
        for(int i=h[u];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(j==father) continue;
            dfs(j,u);
            f[u]+=Math.max((long)0,f[j]);
        }
    }
    
    public static void main(String[] args)
    {
        Scanner sc=new Scanner(System.in);
        Arrays.fill(h,-1);
        n=sc.nextInt();
        for(int i=1;i<=n;i++) w[i]=sc.nextInt();
        
        int t=n-1;
        while(t-->0)
        {
            int a=sc.nextInt(),b=sc.nextInt();
            add(a,b); add(b,a);  
        }
        dfs(1,-1);
        long res=-0x3f3f3f3f;
        for(int i=1;i<=n;i++) res=Math.max(res,f[i]);
        System.out.print(res);
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_61639349/article/details/129737677
4
(4)