题目关键信息:
有n个结点,总共有n-1条边,第i个结点的金币数为图片说明 ,现在要求把n个结点分割成K个连通区域,且每个连通区域的金币之和需要大于等于m
方法一:dfs

  • 用二维数组list存储每个结点和它的相邻结点,visit数组记录结点是否被访问过,res记录分割次数
  • 从根结点开始搜索,遇到访问过的结点直接跳过,未访问过的结点继续搜索,记录当前结点和它相邻结点的金币数,如果金币数大于等于m则分割次数增加,sum置0,为下一次统计连通区域的金币数做准备,返回sum(未达到m时则sum会继续累加直到达到m再分割)
  • 遍历完所有结点后,分割次数大于等于k则可行,否则不可行
    图片说明
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param n int整型 
     * @param k int整型 
     * @param m int整型 
     * @param u int整型一维数组 
     * @param v int整型一维数组 
     * @param x int整型一维数组 
     * @return bool布尔型
     */
    ArrayList<Integer>[]list;
    boolean[]visit;
    ArrayList<Integer>child;
    int res;//统计分割次数
    public boolean solve (int n, int k, int m, int[] u, int[] v, int[] x) {
        // write code here
        list=new ArrayList[n+1];
        child=new ArrayList<>();
        for(int i=1;i<n+1;i++){
            list[i]=new ArrayList<Integer>(n);
        }
        for(int i=0;i<u.length;i++){
            list[u[i]].add(v[i]);
            list[v[i]].add(u[i]);//建图
        }
        visit=new boolean[n+1];//记录结点是否被访问过,避免同个结点被访问多次导致死循环
        dfs(x,k,m,1);//从第一个结点开始搜索
        return res>=k;//分割次数大于等于k则可行
    }
   int dfs(int[]x,int k,int m,int curr){
       int sum=x[curr-1];//记录当前结点的金币数
       for(int c:list[curr]){//访问当前结点的邻接结点
           if(visit[c])continue;//如果结点被访问过则直接跳过
           visit[c]=true;//标记被访问过的结点为true
           sum+=dfs(x,k,m,c);//统计当前结点和它连通结点的金币数
       }
       if(sum>=m){
           sum=0;//如果金币数超过m,sum置为0,不影响下一个连通区域的金币统计
           res++;;//分割次数增加
       }
       return sum;
   }
}

复杂度:
时间复杂度:深度优先搜索每个结点至少访问一次,
空间复杂度:递归栈大小不超过n,二维数组list的大小不超过,图片说明
方法二:bfs

  • 从叶子结点向上搜索,因此需要记录下所有结点的度数,因为是无向图,叶子结点的度数为1,所以找到所有度数为1的结点
  • 将所有叶子结点入队,并且设置为已访问,避免后面访问结点的邻接结点时重复访问叶子结点
  • 需要统计每个结点扩散出去后连通区域金钱数,为了方便记录,将x数组拷贝一份到money数组,从叶子结点开始搜素其邻接结点,记录金钱数到monney数组对应的位置,并且将未访问过的结点设置为已访问并入队
  • 如果出队结点的金钱数已经超过m,说明已找到一个符合条件的连通区域,分割次数res增加,最后分割次数大于等于k,则返回true
    图片说明
import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param n int整型 
     * @param k int整型 
     * @param m int整型 
     * @param u int整型一维数组 
     * @param v int整型一维数组 
     * @param x int整型一维数组 
     * @return bool布尔型
     */
    ArrayList<Integer>[]list;
    boolean[]visit;
    int[]degree;
    int res=0;//统计分割次数
    public boolean solve (int n, int k, int m, int[] u, int[] v, int[] x) {
        // write code here
        list=new ArrayList[n+1];
        degree=new int[n+1];
        for(int i=1;i<n+1;i++){
            list[i]=new ArrayList<Integer>(n);
        }
        for(int i=0;i<u.length;i++){
            list[u[i]].add(v[i]);
            list[v[i]].add(u[i]);//建图
            degree[u[i]]++;//统计各个结点的度数
            degree[v[i]]++;

        }
        visit=new boolean[n+1];//记录结点是否被访问过,避免同个结点被访问多次导致死循环
        return bfs(x,k,m);
    }
   boolean bfs(int[]x,int k,int m){
       Queue<Integer>q=new LinkedList<Integer>();
       int[]money=new int[x.length];
       System.arraycopy(x,0,money,0,x.length);//money数组记录每个结点和周围连通域的金钱值
       for(int i=1;i<degree.length;i++){
           if(degree[i]==1)
           {  q.offer(i);
               visit[i]=true;//将度数为1的叶子结点入队,并且设置为已访问,后面才不会重复入队
           }
       }
       //从叶子结点向上搜索其相邻结点
       while(!q.isEmpty()){
           int node=q.poll();
           if(money[node-1]>=m){
               res++;
               money[node-1]=0;//为下一个连通域做准备
           }
           for(int c:list[node]){
               money[c-1]+=money[node-1];//保存每个结点和周围连通域的金钱值
               if(!visit[c]){
                   visit[c]=true;
                   q.offer(c);//没有访问过的结点才入队,避免重复访问
               }
           }
       }return res>=k;//分割次数大于等于k则可行
   }
}

复杂度:
时间复杂度:,bfs至少访问每个结点一次
空间复杂度:图片说明 ,二维数组list的大小不超过