题解 | #连通块#

连通块

http://www.nowcoder.com/practice/5b2ed1c316fc42a0b974ff2b264fa922

题目关键信息:
有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的大小不超过

全部评论

相关推荐

05-11 11:48
河南大学 Java
程序员牛肉:我是26届的双非。目前有两段实习经历,大三上去的美团,现在来字节了,做的是国际电商的营销业务。希望我的经历对你有用。 1.好好做你的CSDN,最好是直接转微信公众号。因为这本质上是一个很好的展示自己技术热情的证据。我当时也是烂大街项目(网盘+鱼皮的一个项目)+零实习去面试美团,但是当时我的CSDN阅读量超百万,微信公众号阅读量40万。面试的时候面试官就告诉我说觉得我对技术挺有激情的。可以看看我主页的美团面试面经。 因此花点时间好好做这个知识分享,最好是单拉出来搞一个板块。各大公司都极其看中知识落地的能力。 可以看看我的简历对于博客的描述。这个帖子里面有:https://www.nowcoder.com/discuss/745348200596324352?sourceSSR=users 2.实习经历有一些东西删除了,目前看来你的产出其实很少。有些内容其实很扯淡,最好不要保留。有一些点你可能觉得很牛逼,但是面试官眼里是减分的。 你还能负责数据库表的设计?这个公司得垃圾成啥样子,才能让一个实习生介入数据库表的设计,不要写这种东西。 一个公司的财务审批系统应该是很稳定的吧?为什么你去了才有RBAC权限设计?那这个公司之前是怎么处理权限分离的?这些东西看着都有点扯淡了。 还有就是使用Redis实现轻量级的消息队列?那为什么这一块不使用专业的MQ呢?为什么要使用redis,这些一定要清楚, 就目前看来,其实你的这个实习技术还不错。不要太焦虑。就是有一些内容有点虚了。可以考虑从PR中再投一点产出
点赞 评论 收藏
分享
每晚夜里独自颤抖:你cet6就cet6,cet4就cet4,你写个cet证书等是什么意思。专业技能快赶上项目行数,你做的这2个项目哪里能提现你有这么多技能呢
点赞 评论 收藏
分享
评论
点赞
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务