HDU - 4546 比赛难度(Java JS Python)

题目来源

Problem - 4546 (hdu.edu.cn)

题目描述

最近,小明出了一些ACM编程题,决定在HDOJ举行一场公开赛。

假设题目的数量一共是n道,这些题目的难度被评级为一个不超过1000的非负整数,并且一场比赛至少需要一个题,而这场比赛的难度,就是所有题目的难度之和,同时,我们认为一场比赛与本场题目的顺序无关,而且题目也不会重复。

显而易见,很容易得到如下信息:

  • 假设比赛只用1个题目,有n种方案;
  • 假设比赛使用2个题目,有(n-1)*n/2种方案;
  • 假设比赛使用3个题目,有(n-2)*(n-1)*n/6种方案;
  • ............
  • 假设比赛使用全部的n个题目,此时方案只有1种。

经过简单估算,小明发现总方案数几乎是一个天文数字!

为了简化问题,现在小明只想知道在所有的方案里面第m小的方案,它的比赛难度是多少呢?

输入描述

输入数据的第一行为一个整数T(1 <= T <= 20),表示有T组测试数据。

每组测试数据:

  • 第一行为两个整数n, m(0 < n, m <= 10000),表示有n个题目,现在要求第m小方案的比赛难度。
  • 第二行有n个数字,分别表示这n个题目的难度值。

输出描述

对于每组测试数据,输出一行"Case #c: ans"(不包含引号),ans 表示要求的第m小的比赛难度,输入数据保证存在第m小的方案,具体参见样例。

用例

输入2
5 6
1 1 1 1 1
5 25
1 2 3 4 5
输出Case #1: 2
Case #2: 11
说明

题目解析

本题其实就是让我们求解: 全组合中"第m小"组合(按组合之和排大小)。

比如nums = [1,2,3],则全部组合如下:

  • [1]
  • [1,2]
  • [1,3]
  • [1,2,3]
  • [2]
  • [2,3]
  • [3]

其中:

  • 第1小的组合为[1]
  • 第2小的组合为[2]
  • 第3小的组合为[3]
  • 第4小的组合为[1,2]
  • 第5小的组合为[2,3]
  • 第6小的组合为[1,2,3]

由于本题数量级较大,因此如果暴力地求出全组合,然后按组合之和排序,求出第m小的组合,肯定会超时。

如果我们将nums进行升序,比如nums = [1, 2, 3, 4],那么第1小的组合肯定是 [1]。

而第1小组合[1],可以看成是基于一个空组合[],加了一个nums最小元素1产生的。

第2小组合此时就有了两种来源:

  • [1] 组合加入下一个最小元素2,形成[1,2]
  • [] 组合加入下一个最小元素2(理论上,空组合下一个元素应该是1,但是由于空组合合入过1形成了[1]组合,因此下一步应该合入2),形成[2]

对比可得第2小组合是[2]。

此时,我们有了三个最小组合,分别是[],[1],[2],如果在为每个组合标出下一个合入元素的话,则可得信息如下:

  • 当前组合 = [],将要被合入的元素 = 3
  • 当前组合 = [1],将要被合入的元素 = 2
  • 当前组合 = [2],将要被合入的元素 = 3

此时根据”当前组合“、”将被被合入的元素“,我们可以得出将要产生的"新组合":

  • 当前组合 = [],将要被合入的元素 = 3,将要产生的新组合 = [3]
  • 当前组合 = [1],将要被合入的元素 = 2,将要产生的新组合 = [1,2]
  • 当前组合 = [2],将要被合入的元素 = 3,将要产生的新组合 = [2,3]

可以对比出,将要产生的新组合中[3]和[1,2]是本轮最小的,我们可以任选一个作为整体第3小。


假设我们选择[3]作为第3小组合,则组合信息变化如下:

  • 当前组合 = [],将要被合入的元素 = 4
  • 当前组合 = [1],将要被合入的元素 = 2
  • 当前组合 = [2],将要被合入的元素 = 3
  • 当前组合 = [3],将要被合入的元素 = 4

此时再算出新组合信息,如下:

  • 当前组合 = [],将要被合入的元素 = 4,将要产生的新组合 = [4]
  • 当前组合 = [1],将要被合入的元素 = 2,将要产生的新组合 = [1,2]
  • 当前组合 = [2],将要被合入的元素 = 3,将要产生的新组合 = [2,3]
  • 当前组合 = [3],将要被合入的元素 = 4,将要产生的新组合 = [3,4]

可以得出本轮最小,整体第4小组合是[1,2]。


此时组合信息变化如下:

  • 当前组合 = [],将要被合入的元素 = 5
  • 当前组合 = [1],将要被合入的元素 = 2
  • 当前组合 = [2],将要被合入的元素 = 3
  • 当前组合 = [3],将要被合入的元素 = 4
  • 当前组合 = [1,2],将要被加入的元素 = 3

由于nums只有[1,2,3,4]元素,没有5,因此对于组合 [] 来说,已经没有新元素可以合入了,因此我们只需要处理剩下的组合即可

  • 当前组合 = [],处理完毕
  • 当前组合 = [1],将要被合入的元素 = 3,将要产生的新组合 = [1,3]
  • 当前组合 = [2],将要被合入的元素 = 3,将要产生的新组合 = [2,3]
  • 当前组合 = [3],将要被合入的元素 = 4,将要产生的新组合 = [3,4]
  • 当前组合 = [1,2],将要被加入的元素 = 3,将要产生的新组合 = [1,2,3]

可以得出本轮最小,整体第5小组合是[1,3]。

此时组合信息变化如下:

  • 当前组合 = [],处理完毕
  • 当前组合 = [1],将要被合入的元素 = 4
  • 当前组合 = [2],将要被合入的元素 = 3
  • 当前组合 = [3],将要被合入的元素 = 4
  • 当前组合 = [1,2],将要被加入的元素 = 3
  • 当前组合 = [1,3],将要被加入的元素 = 4

按上面逻辑,第m轮得出的最小组合就是第m小的组合。

具体代码实现中,我们可以使用优先队列来找出每轮最小的:”将要产生的新组合“。

而加入优先队列的组合模型设计如下:

  • 当前组合之和 curSum
  • 将要被加入当前组合的新元素索引位置 nextIdx

根据这两个信息可以得出”将要产生的新组合“之和 = curSum + nums[nextIdx]

而对于一个组合模型,其”将要产生的新组合“之和越小,则优先级越高。

JS算法源码

关于JS中优先队列的实现可以参考:LeetCode - 1705 吃苹果的最大数目_leetcode - 1705 吃苹果的最 数 _伏城之外的博客-csdn博客_伏城之外的博客-CSDN博客

const rl = require("readline").createInterface({ input: process.stdin });
var iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;// 输入处理
void (async function () {const t = parseInt(await readline());const ans = [];for (let i = 1; i <= t; i++) {const [n, m] = (await readline()).split(" ").map(Number);const nums = (await readline()).split(" ").map(Number);ans.push(`Case #${i}: ${getResult(n, m, nums)}`);}for (let an of ans) console.log(an);
})();// 算法入口
function getResult(n, m, nums) {nums.sort((a, b) => a - b);// 对于一个组合模型,其”将要产生的新组合“之和越小,则优先级越高// curSum + nums[nextIdx] 为 ”将要产生的新组合“之和const pq = new PriorityQueue((a, b) => a.curSum + nums[a.nextIdx] - (b.curSum + nums[b.nextIdx]));// 空组合的和为0, 将要加入的新元素是nums[0], 即索引0的元素,其将要产生的新组合之和为 0 + nums[0]let c = new CombineModel(0, 0);for (let i = 1; i < m; i++) {// c是当前最小组合模型,最小的组合模型指的是将要产生的新组合之和在对应轮次中最小// 如果当前组合模型c还有可合入的下一个元素,即c.nextIdx + 1 < n, 则说明可以基于当前组合模型产生一个新组合if (c.nextIdx + 1 < n) {// 基于当前组合模型产生的新组合,也是本轮最小的组合,即第 i 小组合pq.offer(new CombineModel(c.curSum + nums[c.nextIdx], c.nextIdx + 1));// 当前组合需要更新nextIdx后,重新加入优先队列c.nextIdx += 1;pq.offer(c);}// 取出优先队列中最小组合(注意这里的最小,指的是基于当前组合,将要产生的新组合之和最小)c = pq.poll();}// 经过m-1轮后, 优先队列中存储的最最小组合模型 得出的 新组合是 第m小组合return c.curSum + nums[c.nextIdx];
}// 组合模型
class CombineModel {constructor(curSum, nextIdx) {this.curSum = curSum; // 当前组合之和this.nextIdx = nextIdx; // 将要被加入当前组合的新元素索引位置}
}// 基于堆实现优先队列
class PriorityQueue {constructor(cpr) {this.queue = [];this.size = 0;this.cpr = cpr;}swap(i, j) {let tmp = this.queue[i];this.queue[i] = this.queue[j];this.queue[j] = tmp;}// 上浮swim() {let ch = this.queue.length - 1;while (ch !== 0) {let fa = Math.floor((ch - 1) / 2);const ch_node = this.queue[ch];const fa_node = this.queue[fa];if (this.cpr(ch_node, fa_node) < 0) {this.swap(ch, fa);ch = fa;} else {break;}}}// 下沉sink() {let fa = 0;while (true) {let ch_left = 2 * fa + 1;let ch_right = 2 * fa + 2;let ch_max;let ch_max_node;const fa_node = this.queue[fa];const ch_left_node = this.queue[ch_left];const ch_right_node = this.queue[ch_right];if (ch_left_node && ch_right_node) {// 注意这里应该要>=0,因为左边优先级高if (this.cpr(ch_left_node, ch_right_node) <= 0) {ch_max = ch_left;ch_max_node = ch_left_node;} else {ch_max = ch_right;ch_max_node = ch_right_node;}} else if (ch_left_node && !ch_right_node) {ch_max = ch_left;ch_max_node = ch_left_node;} else if (!ch_left_node && ch_right_node) {ch_max = ch_right;ch_max_node = ch_right_node;} else {break;}// 注意这里应该要>0,因为父优先级高if (this.cpr(ch_max_node, fa_node) < 0) {this.swap(ch_max, fa);fa = ch_max;} else {break;}}}// 向优先队列中加入元素offer(ele) {this.queue.push(ele);this.size++;this.swim();}// 取出最高优先级元素poll() {this.swap(0, this.queue.length - 1);this.size--;const ans = this.queue.pop();this.sink();return ans;}
}

Java算法源码

import java.util.ArrayList;
import java.util.Arrays;
import java.util.PriorityQueue;
import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc = new Scanner(System.in);ArrayList<String> ans = new ArrayList<>();int t = sc.nextInt();for (int i = 1; i <= t; i++) {int n = sc.nextInt();int m = sc.nextInt();int[] nums = new int[n];for (int j = 0; j < n; j++) {nums[j] = sc.nextInt();}ans.add("Case #" + i + ": " + getResult(n, m, nums));}for (String an : ans) {System.out.println(an);}}static class CombineModel {int curSum; // 当前组合之和int nextIdx; // 将要被加入当前组合的新元素索引位置public CombineModel(int curSum, int nextIdx) {this.curSum = curSum;this.nextIdx = nextIdx;}}public static int getResult(int n, int m, int[] nums) {Arrays.sort(nums);// 对于一个组合模型,其”将要产生的新组合“之和越小,则优先级越高// curSum + nums[nextIdx] 为 ”将要产生的新组合“之和PriorityQueue<CombineModel> pq =new PriorityQueue<>((a, b) -> a.curSum + nums[a.nextIdx] - (b.curSum + nums[b.nextIdx]));// 空组合的和为0, 将要加入的新元素是nums[0], 即索引0的元素,其将要产生的新组合之和为 0 + nums[0]CombineModel c = new CombineModel(0, 0);for (int i = 1; i < m; i++) {// c是当前最小组合模型,最小的组合模型指的是将要产生的新组合之和在对应轮次中最小// 如果当前组合模型c还有可合入的下一个元素,即c.nextIdx + 1 < n, 则说明可以基于当前组合模型产生一个新组合if (c.nextIdx + 1 < n) {// 基于当前组合模型产生的新组合,也是本轮最小的组合,即第 i 小组合pq.offer(new CombineModel(c.curSum + nums[c.nextIdx], c.nextIdx + 1));// 当前组合需要更新nextIdx后,重新加入优先队列c.nextIdx += 1;pq.offer(c);}// 取出优先队列中最小组合(注意这里的最小,指的是基于当前组合,将要产生的新组合之和最小)c = pq.poll();}// 经过m-1轮后, 优先队列中存储的最最小组合模型 得出的 新组合是 第m小组合return c.curSum + nums[c.nextIdx];}
}

Python算法源码

import queuedef getResult(n, m, nums):nums.sort()pq = queue.PriorityQueue()class CombineModel:def __init__(self, curSum, nextIdx):self.curSum = curSum  # 当前组合之和self.nextIdx = nextIdx  # 将要被加入当前组合的新元素索引位置def __lt__(self, other):# 对于一个组合模型,其”将要产生的新组合“之和越小,则优先级越高# curSum + nums[nextIdx] 为 ”将要产生的新组合“之和return self.curSum + nums[self.nextIdx] < (other.curSum + nums[other.nextIdx])# 空组合的和为0, 将要加入的新元素是nums[0], 即索引0的元素,其将要产生的新组合之和为 0 + nums[0]c = CombineModel(0, 0)for _ in range(1, m):# c是当前最小组合模型,最小的组合模型指的是将要产生的新组合之和在对应轮次中最小# 如果当前组合模型c还有可合入的下一个元素,即c.nextIdx + 1 < n, 则说明可以基于当前组合模型产生一个新组合if c.nextIdx + 1 < n:# 基于当前组合模型产生的新组合,也是本轮最小的组合,即第 i 小组合pq.put(CombineModel(c.curSum + nums[c.nextIdx], c.nextIdx + 1))# 当前组合需要更新nextIdx后,重新加入优先队列c.nextIdx += 1pq.put(c)# 取出优先队列中最小组合(注意这里的最小,指的是基于当前组合,将要产生的新组合之和最小)c = pq.get()# 经过m-1轮后, 优先队列中存储的最最小组合模型 得出的 新组合是 第m小组合return c.curSum + nums[c.nextIdx]t = int(input())ans = []for i in range(t):n, m = map(int, input().split())nums = list(map(int, input().split()))ans.append(f"Case #{i+1}: {getResult(n, m, nums)}")for an in ans:print(an)

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

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

相关文章

实战:Docker+Jenkins+Gitee构建CICD流水线

文章目录 前言Jenkins部署创建Jenkins docker-compose配置maven源启动Jenkins容器安装插件Gitee ssh公匙配置与测试项目提交 Jenkins创建流水线写在最后 前言 持续集成和持续交付一直是当下流行的开发运维方式&#xff0c;CICD省去了大量的运维时间&#xff0c;也能够提高开发…

ElasticSearch基本使用--ElasticSearch文章一

文章目录 官网学习必要性elasticsearch/kibana安装版本数据结构说明7.x版本说明ElasticSearch kibana工具测试后续我们会一起分析 官网 https://www.elastic.co/cn/ 学习必要性 1、在当前软件行业中&#xff0c;搜索是一个软件系统或平台的基本功能&#xff0c; 学习Elastic…

Git克隆文件不显示绿色勾、红色感叹号等图标

1、问题 Git和TorToiseGit安装后&#xff0c;Git克隆的文件不会显示绿色勾、红色感叹号等图标。 2、检查注册表 2.1、打开注册表 (1)WinR打开运行窗口&#xff0c;输入regedit&#xff0c;点击确定&#xff0c;打开注册表编辑器。 2.2、找如下路径 (1)找到路径 计算机\HKEY_…

linux----vim的使用

vi和vim是Linux下的一个文本编辑工具&#xff0c;最小化安装只有vi vim&#xff0c;需要额外安装&#xff0c;比vi更强大一些 # vim 操作文件&#xff0c;有三种模式&#xff1a;普通模式&#xff0c;编辑模式&#xff0c;命令模式 -vim 文件名刚进来----》普通模式--》只…

VBA技术资料MF34:检查Excel自动筛选是否打开

【分享成果&#xff0c;随喜正能量】聪明人&#xff0c;抬人不抬杠&#xff1b;傻子&#xff0c;抬杠不抬人。聪明人&#xff0c;把别人抬得很高&#xff0c;别人高兴、舒服了&#xff0c;看你顺眼了&#xff0c;自然就愿意帮你&#xff01;而傻人呢&#xff1f;不分青红皂白&a…

golang sync.singleflight 解决热点缓存穿透问题

在 go 的 sync 包中&#xff0c;有一个 singleflight 包&#xff0c;里面有一个 singleflight.go 文件&#xff0c;代码加注释&#xff0c;一共 200 行出头。内容包括以下几块儿&#xff1a; Group 结构体管理一组相关的函数调用工作,它包含一个互斥锁和一个 map,map 的 key 是…

Golang之路---01 Golang的安装与配置

Golang之路—01 Golang语言安装与配置 官网上下载Windows环境下的安装包 官网下载地址 双击下载后的文件进行安装&#xff0c;可根据需要自定义选择解压后的文件位置。 接着新创建一个文件夹&#xff0c;保存Golang语言项目。 在里面新建bin,pkg,src三个文件夹。 环境变量…

Verilog语法学习——LV4_移位运算与乘法

LV4_移位运算与乘法 题目来源于牛客网 [牛客网在线编程_Verilog篇_Verilog快速入门 (nowcoder.com)](https://www.nowcoder.com/exam/oj?page1&tabVerilog篇&topicId301) 题目 题目描述&#xff1a; 已知d为一个8位数&#xff0c;请在每个时钟周期分别输出该数乘1/…

Linux:Linux的发展史和作用有哪些?

文章目录 Linux是什么&#xff1f;Linux的开源特征为什么要学习Linux&#xff1f;Linux的应用场景有哪些&#xff1f; Linux是什么&#xff1f; 简单来说&#xff0c;Linux就是操作系统&#xff0c;它和Windows等软件一样&#xff0c;都只是操作系统&#xff0c;并无区别 Linu…

CSS的一些基础知识

选择器&#xff1a; 选择器用于选择要应用样式的HTML元素。常见的选择器包括标签选择器&#xff08;如 div、p&#xff09;、类选择器&#xff08;如 .class&#xff09;、ID选择器&#xff08;如 #id&#xff09;和伪类选择器&#xff08;如 :hover&#xff09;。选择器可以根…

webpack优化前端框架性能

webpack优化目的 webpack优化目的1. 提升开发体验提升开发体验使用 SourceMap 2. 提升打包构建速度提升打包构建速度&#xff08;开发模式&#xff09;提升打包速度 oneOf提升打包速度 include&#xff08;包含&#xff09;/exclude&#xff08;排除&#xff09;提升第二次打包…

爬虫-requests-cookie登录古诗文网

一、前言 1、requests简介 requests是一个很实用的Python HTTP客户端库&#xff0c;爬虫和测试服务器响应数据时经常会用到&#xff0c;它是python语言的第三方的库&#xff0c;专门用于发送HTTP请求&#xff0c;使用起来比urllib更简洁也更强大。 2、requests的安装 pip i…

电脑选睡眠、休眠还是关机?

关机 这是大家最熟悉的。关机时&#xff0c;系统首先关闭所有运行中的程序&#xff0c;然后关闭系统后台服务。随后&#xff0c;系统向主板请求关机&#xff0c;主板断开电源的供电使能&#xff0c;让电源切断对绝大多数设备的供电&#xff08;只剩一些内部零件仍会维持电源供应…

华为刷题:HJ3明明随机数

import java.util.Scanner;// 注意类名必须为 Main, 不要有任何 package xxx 信息 public class Main {public static void main(String[] args) {Scanner scan new Scanner(System.in);int N scan.nextInt();int[] arr new int[N];for (int i 0; i < N; i) {int n sca…

Materials - UE5中的PivotPainter

个人学习笔记的归档和发表&#xff1b;文中所有案例都来自官方的ContentExample中的PivotPainter相关关卡&#xff1b; 可以使用3DS Max Script中的脚本&#xff08;Pivot Painter&#xff09;对模型进行处理&#xff0c;让每个Element都有自己的Pivot Point&#xff0c;来方便…

PostgreSQL 简洁、使用、正排索引与倒排索引、空间搜索、用户与角色

PostgreSQL使用 PostgreSQL 是一个免费的对象-关系数据库服务器(ORDBMS)&#xff0c;在灵活的BSD许可证下发行。PostgreSQL 9.0 &#xff1a;支持64位windows系统&#xff0c;异步流数据复制、Hot Standby&#xff1b;生产环境主流的版本是PostgreSQL 12 BSD协议 与 GPL协议 …

SQL server 文件占用硬盘过大 日志 读写分离同步文件过大清理 DBCC收缩数据库 分发数据库distribution收缩

一顿操作猛如虎 又省出好几十G硬盘空间 小破站又能蹦跶了 目标&#xff1a;实例库日志压缩清理,分发数据库压缩清理 采用SQL 脚本收缩数据库 截断事务日志 backup log [数据库名] with no_log收缩数据库 dbcc shrinkdatabase ([数据库名]) 4.以上操作都不行的话&#xff0…

物联网场景中的边缘计算解决方案有哪些?

在物联网场景中&#xff0c;边缘计算是一种重要的解决方案&#xff0c;用于在物联网设备和云端之间进行实时数据处理、分析和决策。HiWoo Box作为工业边缘网关设备&#xff0c;具备边缘计算能力&#xff0c;包括单点公式计算、Python脚本编程以及规则引擎&#xff0c;它为物联网…

华为云NFS使用API删除大文件目录

最近在使用华为云SFS时&#xff0c;如果一个目录存储文件数超过100W&#xff0c;执行 “rm -rf path”时&#xff0c;存在删不动的情况&#xff0c;可以使用华为云API接口&#xff0c;执行异步删除。 华为官网&#xff1a; 删除文件系统目录_弹性文件服务 SFS_API参考_SFS Tu…

高效率,38V最大输入单电感同步升/降稳压器SYV939C

SYV939是一种高压同步降压-升压转换器。该器件工作在4V至28V的宽输入电压范围内&#xff0c;具有10max平均电感电流能力。四个集成的低RDS(ON)开关最大限度地减少了传导损耗。 SYV939c包括完整的保护功能&#xff0c;如输出过流/短路保护&#xff0c;过压保护和热停机&#xff…