LC-1595. 连通两组点的最小成本(状态压缩DP(记忆化搜索==>动态规划))

news2024/11/18 16:28:33

1595. 连通两组点的最小成本

难度困难86

给你两组点,其中第一组中有 size1 个点,第二组中有 size2 个点,且 size1 >= size2

任意两点间的连接成本 cost 由大小为 size1 x size2 矩阵给出,其中 cost[i][j] 是第一组中的点 i 和第二组中的点 j 的连接成本。**如果两个组中的每个点都与另一组中的一个或多个点连接,则称这两组点是连通的。**换言之,第一组中的每个点必须至少与第二组中的一个点连接,且第二组中的每个点必须至少与第一组中的一个点连接。

返回连通两组点所需的最小成本。

示例 1:

img

输入:cost = [[15, 96], [36, 2]]
输出:17
解释:连通两组点的最佳方法是:
1--A
2--B
总成本为 17 。

示例 2:

img

输入:cost = [[1, 3, 5], [4, 1, 1], [1, 5, 3]]
输出:4
解释:连通两组点的最佳方法是:
1--A
2--B
2--C
3--A
最小成本为 4 。
请注意,虽然有多个点连接到第一组中的点 2 和第二组中的点 A ,但由于题目并不限制连接点的数目,所以只需要关心最低总成本。

示例 3:

输入:cost = [[2, 5, 1], [3, 4, 7], [8, 1, 2], [6, 2, 4], [3, 8, 8]]
输出:10

提示:

  • size1 == cost.length
  • size2 == cost[i].length
  • 1 <= size1, size2 <= 12
  • size1 >= size2
  • 0 <= cost[i][j] <= 100

状压DP(记忆化搜索 ==> 动态规划)

https://leetcode.cn/problems/minimum-cost-to-connect-two-groups-of-points/solution/jiao-ni-yi-bu-bu-si-kao-dong-tai-gui-hua-djxq/

class Solution {
    int n, m;
    int[][] cache;
    List<List<Integer>> cost;
    public int connectTwoGroups(List<List<Integer>> cost) {
        this.cost = cost;
        n = cost.size(); m = cost.get(0).size();
        cache = new int[n][1 << m];
        for(int i = 0; i < n; i++)
            Arrays.fill(cache[i], -1);
        return dfs(n-1, 0);
    }
    // 定义dfs(i, mask) 表示 在第一组中还有0-i个点需要连接,连接的第二组点在集合mask中,所需要的最小成本
    // 转移:枚举第一组第i个点连接第二组中的任意一个点
    // 递归边界: dfs(0, mask) ,此时mask中还为0的点可以连接任意第一组的点,选最小的成本
    // 递归入口: dfs(全集,0)
    public int dfs(int i, int mask){
        if(i < 0){
            if(mask == (1 << m) - 1)
                return 0; // 第二组的点都选过了,没有额外成本
            int ans = 0; // 寻找第二组中不为1的点,可以连接第一组任意点,选成本最小的点
            for(int j = 0; j < m; j++){
                if(((mask >> j) & 1) == 1) continue;
                int min = Integer.MAX_VALUE;
                for(int k = 0; k < n; k++){
                    min = Math.min(min, cost.get(k).get(j));
                }
                ans += min;
            }
            return ans;
        }
        if(cache[i][mask] >= 0) return cache[i][mask];
        int ans = Integer.MAX_VALUE;
        // 枚举第i位选第二组的哪个
        for(int p = 0; p < m; p++){
            // 将第一组中的i和第二组中的p连接起来,代价为cost.get(i).get(p)
            ans = Math.min(ans, cost.get(i).get(p) + dfs(i-1, mask | (1 << p)));
        }
        return cache[i][mask] = ans;
    }
}

优化:这里有重复寻找第二组中每个点成本最小的连接方式,可以进行预处理

class Solution {
    int n, m;
    int[][] cache;
    int[] mincost;
    List<List<Integer>> cost;
    public int connectTwoGroups(List<List<Integer>> cost) {
        this.cost = cost;
        n = cost.size(); m = cost.get(0).size();
        // 预处理寻找第二组中每个点的最小连接成本
        mincost = new int[m];
        Arrays.fill(mincost, Integer.MAX_VALUE);
        for(int i = 0; i < m; i++){
            for(List<Integer> c : cost){
                mincost[i] = Math.min(mincost[i], c.get(i));
            }
        }
        cache = new int[n][1 << m];
        for(int i = 0; i < n; i++)
            Arrays.fill(cache[i], -1);
        return dfs(n-1, 0);
    }
    // 定义dfs(i, mask) 表示 在第一组中还有0-i个点需要连接,连接的第二组点在集合mask中,所需要的最小成本
    // 转移:枚举第一组第i个点连接第二组中的任意一个点
    // 递归边界: dfs(0, mask) ,此时mask中还为0的点可以连接任意第一组的点,选最小的成本
    // 递归入口: dfs(全集,0)
    public int dfs(int i, int mask){
        if(i < 0){
            if(mask == (1 << m) - 1)
                return 0; // 第二组的点都选过了,没有额外成本
            int ans = 0; // 寻找第二组中不为1的点,可以连接第一组任意点,选成本最小的点
            for(int j = 0; j < m; j++){
                if(((mask >> j) & 1) == 1) continue;
                ans += mincost[j];
            }
            return ans;
        }
        if(cache[i][mask] >= 0) return cache[i][mask];
        int ans = Integer.MAX_VALUE;
        // 枚举第i位选第二组的哪个
        for(int p = 0; p < m; p++){
            // 将第一组中的i和第二组中的p连接起来,代价为cost.get(i).get(p)
            ans = Math.min(ans, cost.get(i).get(p) + dfs(i-1, mask | (1 << p)));
        }
        return cache[i][mask] = ans;
    }
}

问: 能不能枚举第二组的点,去连接第一组的点?

答: 也可以,但这样做的时间复杂度是 O(nm2^n),相比 O(nm2^m)更慢。注意本题 n >= m。

记忆化搜索转递推

class Solution {
    // 在记忆化搜索中,存在一个状态 i < 0, 因此f数组整体右移
    //      令f[0][x]表示状态 i < 0,最后返回结果f[n][(1 << m) - 1]
    public int connectTwoGroups(List<List<Integer>> cost) {
        int n = cost.size(), m = cost.get(0).size();
        // 预处理寻找第二组中每个点的最小连接成本
        int[] mincost = new int[m];
        Arrays.fill(mincost, Integer.MAX_VALUE);
        for(int i = 0; i < m; i++){
            for(List<Integer> c : cost){
                mincost[i] = Math.min(mincost[i], c.get(i));
            }
        }
        int[][] f = new int[n+1][1 << m];
        for(int i = 0; i < (1 << m); i++)
            for(int j = 0; j < m; j++)
                if((i >> j & 1) == 1) // 第二组的点 k 未连接
                    f[0][i] += mincost[j]; // 去第一组找个成本最小的点连接
    
        for(int i = 0; i < n; i++)
            for(int j = 0; j < (1 << m); j++){
                int res = Integer.MAX_VALUE;
                for(int k = 0; k < m; k++) // 第一组的点 i 与第二组的点 k
                    res = Math.min(res, f[i][j & ~(1 << k)] + cost.get(i).get(k));
                f[i+1][j] = res;
            }
        return f[n][(1 << m) - 1];
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/692022.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

FineReport学习3

冻结行列&#xff08;冻结表头&#xff09; 创建多数据源之间的关联 给单元格添加过滤条件&#xff0c;让 ds2 的「客户ID」等于 ds1 的「客户ID」&#xff0c;这样两数据集就关联起来 复杂多源报表

.NETCore项目在Windows下构建Docker镜像并本地导出分发到CentOS系统下

在Windows下使用Docker&#xff0c;我们选择Docker Desktop这个软件&#xff0c;非常方便。 Docker Desktop介绍及安装 Docker Desktop是适用于Mac、Linux或Windows环境的一键安装应用程序&#xff0c;使您能够构建和共享容器化应用程序和微服务。 它提供了一个简单的GUI&…

[数据库系统] 五、数据增删改

第一关&#xff1a;数据插入 用insert给数据库添加数据 相关知识 有关系student(sno,sname,ssex,sage,sdept)&#xff0c;属性对应含义&#xff1a;学号&#xff0c;姓名&#xff0c;性别&#xff0c;所在系。现有的部分元组如下所示 insert 向数据库表插入数据的基本格式有…

生成 ocr key 字符集 alphabet 6698个字符

生成 ocr key 字符集 alphabet import pickle as pkl#----------- 生成 ocr key 字符集 alphabet alphabet_set set() # 数据集label infofiles_label [/home/jlb/下载/rec_data_lesson_demo/train.txt, /home/jlb/下载/rec_data_lesson_demo/val.txt]# ppocr中文key infofil…

SpringBoot—热部署(Community)

配置热部署 &#x1f50e;引入依赖&#x1f50e;设置 Settings 文章介绍的是社区版 Idea 配置热部署的步骤 &#x1f50e;引入依赖 复制如下代码至 pom.xml 文件中即可 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-b…

无人机遥感在农林信息提取中的实现方法与GIS融合制图教程

详情点击链接&#xff1a;无人机遥感在农林信息提取中的实现方法与GIS融合制图 遥感技术作为一种空间大数据手段&#xff0c;能够从多时、多维、多地等角度&#xff0c;获取大量的农情数据。数据具有面状、实时、非接触、无伤检测等显著优势&#xff0c;是智慧农业必须采用的重…

【夏季旅行团建】趣玩千岛湖-环湖骑行闯关-卡丁车上演速度与激情-刺激高空体验,杭州周边江浙沪团建好去处

千岛湖的秀丽景色让人惊艳&#xff0c;也是江浙沪地区热门的团建目的地之一&#xff1b; 千岛湖隶属于淳安县。从杭州自驾2个小时&#xff0c;高铁1小时&#xff0c;属于国家5A级别的人工湖&#xff0c;前身是新安江水库。拥有星罗棋布的1078个岛屿&#xff0c;是世界上岛屿最多…

AndroidStudio xml布局文件输入没有提示

AndroidStudio xml布局文件输入没有提示&#xff0c;如下图&#xff1a; 原因是老的AndroidStudio与新的sdk版本不一致 方法1&#xff1a;修改compileSdkVersion低于33即可&#xff0c;不建议 方法2&#xff1a;升级AndroidStudio版本&#xff0c;建议 如下是我的AndroidStu…

详解序列化

目录 1.什么是序列化 2.JAVA中的序列化 2.1.成员变量必须可序列化 2.2.transient关键字&#xff0c;可避免被序列化 2.3.无法更新状态 2.4.serialVersionUID 3.JDK序列化算法 4.序列化在实际中的一些应用 1.什么是序列化 序列化就是将对象转换为二进制格式的过程。对象…

MySQL的MMM高可用

MySQL的MMM高可用 一、MMM简介二、搭建MySQL的MMM高可用1、准备环境2、 搭建 MySQL 多主多从模式3、安装配置 MySQL-MMM4、故障测试5、客户端测试 一、MMM简介 MMM&#xff08;Master-Master replication manager for MvSQL&#xff0c;MySQL主主复制管理器&#xff09;是一套…

访问学者申请英语口语怎样顺利通关

想要成功申请成为访问学者&#xff0c;英语口语的流利表达是非常重要的。下面知识人网小编整理的一些帮助你顺利通关的建议&#xff1a; 1. 提前准备&#xff1a;在面试之前&#xff0c;充分准备各种常见问题的回答。练习口语表达&#xff0c;加强词汇和语法的掌握。可以通过与…

如何学习PHP语法和基本概念? - 易智编译EaseEditing

学习PHP语法和基本概念是成为一个合格的PHP开发者的第一步。以下是一些学习PHP语法和基本概念的建议&#xff1a; 官方文档&#xff1a; PHP官方提供了详细的文档&#xff0c;包括语言参考、函数参考和示例等。阅读官方文档是学习PHP语法和基本概念的最佳途径。你可以访问PHP…

企业如何数字化转型?企业数字化转型面临哪些挑战?

一文看懂&#xff1a;传统企业数字化转型会面临哪些困难与挑战&#xff1f;如何有效解决&#xff1f; 目前&#xff0c;数字技术的发展已经从互联网、大数据&#xff0c;迈入了AI人工智能时代。而企业也如逆水行舟、不进则退&#xff0c;如果不进行数字化转型&#xff0c;企业…

AI芯片的基础

前置基础 AI芯片其实就是AI算法的专用处理器&#xff0c;像CPU的话是一个通用处理器&#xff0c;CPU按照逻辑可以分为三个模块&#xff1a;控制模块&#xff0c;运算模块&#xff0c;存储模块&#xff1b;其中控制单元有指令寄存器和指令译码器&#xff0c;根据用户预先编译好…

关于Apache Dubbo反序列化漏洞(CVE-2023-23638)的预警提示与对应的Zookeeper版本

公司在升级dubbo过程中因zookeeper版本不匹配&#xff0c;导致服务注册和调用出现异常 一、漏洞详情 Apache Dubbo是一款高性能、轻量级的开源Java服务框架。 Apache官方发布安全公告&#xff0c;修复了Apache Dubbo中的一个反序列化漏洞&#xff08;CVE-2023-23638&#xff…

系统架构设计师 5:软件工程

一、软件工程 1 软件过程模型 软件要经历从需求分析、软件设计、软件开发、运行维护&#xff0c;直至被淘汰这样的全过程&#xff0c;这个全过程称为软件的生命周期。 为了使软件生命周期中的各项任务能够有序地按照规程进行&#xff0c;需要一定的工作模型对各项任务给予规…

不会设计也能轻松制作商品宣传海报图片,只要跟着这个教程走

促销活动是商家吸引顾客的重要方式之一&#xff0c;而宣传海报则是促销活动中的主要宣传工具之一。好的宣传海报可以让顾客对活动产生兴趣&#xff0c;提高产品的曝光率。然而&#xff0c;对于小型商家来说&#xff0c;往往没有专业的设计人员&#xff0c;如何制作出符合自己需…

项目开发中的安全问题怎么处理?

目录 1.客户的数据不可信 2. 客户端提交的参数需要校验 3.请求头里的内容出现错误 1.客户的数据不可信 PostMapping("/order") public void wrong(RequestBody Order order) {this.createOrder(order); } 对应的实体类如下&#xff1a; Data public class Order …

[框架]MyBatis框架

关于MyBatis框架 MyBatis框架的主要作用&#xff1a;实现并简化数据库编程。 MyBatis框架的依赖项 MyBatis框架的依赖项是&#xff1a;mybatis&#xff0c;但&#xff0c;通常还应该再添加&#xff1a;mybatis-spring、spring-jdbc、mysql或其它数据库的依赖项、数据库连接池…

FreeRTOS_列表和列表项

目录 1. 什么是列表和列表项&#xff1f; 1.1 列表 1.2 列表项 1.3 迷你列表项 2. 列表和列表项初始化 2.1 列表初始化 2.2 列表项初始化 3. 列表项插入 3.1 列表项插入函数分析 3.2 列表项插入过程图示 3.2.1 插入值为 40 的列表项 3.2.2 插入值为 60 的列表项 3…