《从入门到精通:蓝桥杯编程大赛知识点全攻略》(六)-分巧克力、K倍区间

embedded/2025/1/24 15:11:54/

前言

在本博客中,我们将讨论两种常见的算法技巧——二分法和前缀和,并通过实际问题来进行应用。首先,我们会通过二分法解决一个经典的“巧克力分配问题”,该问题需要我们在不同的分配方案中找到最优的解。接着,我们将使用前缀和来解决一个关于区间问题的挑战——计算给定区间内能够被 k 整除的子数组的数量。这两个问题通过不同的算法技巧为我们提供了高效求解复杂问题的思路。接下来,进入具体的解决方案和代码实现。


分巧克力

儿童节那天有 K位小朋友到小明家做客。小明拿出了珍藏的巧克力招待小朋友们。小明一共有 N 块巧克力,其中第 i块是 Hi×Wi 的方格组成的长方形。
为了公平起见,小明需要从这 N块巧克力中切出 K块巧克力分给小朋友们。
切出的巧克力需要满足:

  1. 形状是正方形,边长是整数
  2. 大小相同

例如一块 6×5的巧克力可以切出 6块 2×2的巧克力或者 2块 3×3的巧克力。
当然小朋友们都希望得到的巧克力尽可能大,你能帮小明计算出最大的边长是多少么?
输入格式
第一行包含两个整数 N 和 K。
以下 N 行每行包含两个整数 Hi和 Wi。
输入保证每位小朋友至少能获得一块 1×1的巧克力。
输出格式
输出切出的正方形巧克力最大可能的边长。
数据范围
1≤N,K≤100000
,1≤Hi,Wi≤100000
输入样例:

java">2 10
6 5
5 6

输出样例

java">2

算法思路

在这里插入图片描述
根据上述图示,我们可以很清楚的得到一个规律,切的正方形的巧克力的边长越长,块数越少。并且得到的巧克力的块数是大巧克力的长 / 正方形巧克力的边长 * 大巧克力的宽 / 正方形巧克力的边长(例如样例 6 / 2 * 5 / 2 = 6);此时就可以通过二分来进行处理,找到块数大于k且正方形巧克力的边长最大值。
用整型变量n记录大巧克力的块数,k记录最小需要多少块巧克力,整型数组h用来记录大巧克力的长,整型数组w来记录大巧克力的宽;题目保证每位小朋友至少能获得一块 1×1的巧克力,所以答案最小值是1,最大值就是大巧克力的最大边长100000;故左边界left = 1,right = 100000。
进行二分查找,判断条件做一个函数check,传递的参数是要切的巧克力的边长mid,整型变量res来记录最后切的巧克力的块数。然后遍历每个大巧克力即(res += (h[i] / mid) * (w[i] / mid)),当res >= k时说明条件成立返回true,小于k时返回fasle。
此时判断条件函数已经写好,当切好的巧克力的块数大于k时,又因为我们需要边长尽可能大,所以块数应该减少,故左区间右移即left = mid;当切好的巧克力的块数小于k时,说明切巧克力的边长应该减少让块数向k接近,故右区间左移即right = mid - 1。最后的答案left就是我们最后需要的巧克力块数最少是k且边长的最大值。

上述二分存在一种情况,当区间长度只有2个元素时即L = R -1,中点mid = (L + R)/ 2 = L,但是答案在右区间故需要左区间右移,但tttL = mid= L形成死循环,故mid = (L + R + 1)/ 2 可以避免这种情况。

代码如下

java">
import java.io.*;
public class Main {static PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));static StreamTokenizer st = new StreamTokenizer(br);static int N = 100010;static int n,k;//长度static int[] h = new int[N];//宽度static int[] w = new int[N];public static void main(String[] args)throws Exception {n = nextInt();k = nextInt();for(int i = 0;i < n;i++){h[i] = nextInt();w[i] = nextInt();}int left = 1;int right = 100000;while(left < right){int mid = (left + right + 1)/2;if(check(mid)){left = mid;}else {right = mid - 1;}}pw.println(left);pw.flush();}public static boolean check(int mid){int res = 0;for(int i = 0;i < n;i++){res += (h[i] / mid) * (w[i] / mid);if(res >= k){return true;}}return false;}public static int nextInt()throws Exception{st.nextToken();return (int)st.nval;}
}

前缀和知识点可以看我的这两篇博客(一维数组的前缀和 、二维数组的前缀和和子矩阵的和)

K倍区间

给定一个长度为 N的数列,A1,A2,…AN,如果其中一段连续的子序列 Ai,Ai+1,…Aj
之和是 K 的倍数,我们就称这个区间 [i,j] 是 K 倍区间。
你能求出数列中总共有多少个 K 倍区间吗?
输入格式
第一行包含两个整数 N 和 K。
以下 N 行每行包含一个整数 Ai。
输出格式
输出一个整数,代表 K倍区间的数目。
数据范围
1≤N,K≤100000,1≤Ai≤100000
输入样例

java">5 2
1
2
3
4
5

输出样例

java">6

算法思路

在这里插入图片描述

这道题的暴力做法(会超时):
枚举每一个终点right从1到n,然后枚举每一个起点left从1到right,再用前缀和数组求区间和,最后%k求余数是否为0。
接下来进行优化:
在R固定的时候,有多少个L满足(s[R] - s[L - 1]) % k == 0是内层循环的含义。可以转化为当R固定时,在0 ~ R - 1之间找到有多少个L满足(s[R] - s[L]) % k == 0可转换为:s[R] % k == s[L] % k即有多少个S[L]与S[R]对k的余数相同。
定义一个整型数组cnt,cnt[i]表示余数为i的数有多少个。
res 用于统计满足条件的子数组数量。
cnt[0] = 1 初始化,因为 s[0] % k == 0 是合法的,表示从数组的开头到某个位置的和能被 k 整除。
for 循环枚举每个可能的右边界 R,根据当前的前缀和 s[R] % k 来判断是否有子数组和能被 k 整除。
如果 cnt[s[R] % k] 大于 0,说明之前有某些前缀和余数与当前前缀和余数相同,从而形成了一个符合条件的子数组。
每次发现符合条件的子数组后,更新 cnt[s[R] % k]。

代码如下

暴力做法(会超时)

java">import java.util.Scanner;public class Main {static int N = 100010;static int[] a = new int[N]; // 原数组static int[] s = new int[N]; // 前缀和数组public static void main(String[] args) {Scanner sc = new Scanner(System.in);int n = sc.nextInt();int k = sc.nextInt();for (int i = 1; i <= n; i++) {a[i] = sc.nextInt();s[i] = s[i - 1] + a[i]; // 公式一}int res = 0;for (int right = 1; right <= n; right++) { // 右端点for (int left = 1; left <= right; left++) { // 左端点 int sum = s[right] - s[left - 1]; // 公式二if (sum % k == 0) res++;}}System.out.print(res);}
}

最终优化版本

java">import java.io.*;public class Main {static PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));static StreamTokenizer st = new StreamTokenizer(br);static int N = 100010;static int[] cnt = new int[N];//前缀和数组static long[] s = new long[N];public static void main(String[] args)throws Exception {int n = nextInt();int k = nextInt();for(int i = 1;i <= n;i++){s[i] = s[i-1] + nextInt();}long res = 0;//cnt[0] = 1 初始化,因为 s[0] % k == 0 是合法的,表示从数组的开头到某个位置的和能被 k 整除。cnt[0] = 1;for(int right = 1;right <= n;right++){//如果 cnt[s[right] % k] 大于 0,说明之前有某些前缀和余数与当前前缀和余数相同,从而形成了一个符合条件的子数组。res += cnt[(int)(s[right] % k)];cnt[(int)(s[right] % k)]++;}pw.println(res);pw.flush();}public static int nextInt()throws Exception{st.nextToken();return (int)st.nval;}
}

总结

通过本博客,我们了解了如何使用二分法解决巧克力分配问题,避免了暴力搜索所带来的高时间复杂度,并通过前缀和巧妙地解决了k倍区间问题,大大提高了计算效率。这两种方法不仅能帮助我们更好地理解算法的优化技巧,也为解决其他类型的问题提供了宝贵的思路。掌握这些技巧后,我们可以更加高效地应对各种复杂算法挑战。


http://www.ppmy.cn/embedded/156600.html

相关文章

RCWL-93000一款微波雷达传感器模块

RCWL-93000是一款微波雷达传感器模块&#xff0c;它主要用于检测物体的移动。以下是对RCWL-93000的详细介绍&#xff1a; 一、基本特性 工作原理&#xff1a;该传感器基于多普勒效应原理工作&#xff0c;当物体在雷达波的覆盖范围内移动时&#xff0c;会反射雷达波&#xff0…

python 列表属性函数及列表查找元素函数

python 列表属性函数及列表查找元素函数 列表可以一次性存储多个数据&#xff0c;且可以为不同的数据类型 语法&#xff1a; [数据1&#xff0c;数据2&#xff0c;数据3,…] 例如&#xff1a; listdict [{city: 大兴安岭春, min_temp: -30}, {city: 呼伦贝尔, min_temp: -29…

进阶——第十六届蓝桥杯(sscanf的运用)

声明变量 char tx_buf[30];char rx_buf[30];char car_type[5];char car_num[5];char car_time[15]; int sscanf(const char *str, const char *format,...); sscanf函数功能 sscanf 函数从字符串 str 中读取数据&#xff0c;根据 format 所指定的格式将数据存储到后续的变量中…

从语音识别到图像识别:AI如何“看”和“听”

引言 随着人工智能技术的不断进步&#xff0c;AI的“听”和“看”能力正变得越来越强大。从语音识别到图像识别&#xff0c;AI不仅能够通过声音与我们互动&#xff0c;还能通过视觉理解和分析周围的世界。这些技术不仅改变了我们与机器的交互方式&#xff0c;也在各行各业中带…

Excel的配置-开放的XML文件

目的 为什么说Excel的配置呢&#xff1f;就是因为很多程序&#xff0c;都是通过Excel导出数据或者图表之类的东西&#xff0c;那如何导出呢&#xff1f;所以必须了解Excel本身的情况。 虽然现在有第三方的工具&#xff0c;但如何想实现自定义的或者复杂的功能&#xff0c;还是…

蓝桥杯lesson3---string的使用

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” string的概念 string字符串是一种更加高级的封装&#xff0c;string字符串中包含了大量的方法&#xff0c;这些方法使得字符串的操作变得更加简单&#xff0c;string的使用&…

【Java计算机毕业设计】基于Springboot+Vue汽车租赁管理系统【源代码+数据库+LW文档+开题报告+答辩稿+部署教程+代码讲解】

源代码数据库LW文档&#xff08;1万字以上&#xff09;开题报告答辩稿 部署教程代码讲解代码时间修改教程 一、开发工具、运行环境、开发技术 开发工具 1、操作系统&#xff1a;Window操作系统 2、开发工具&#xff1a;IntelliJ IDEA或者Eclipse 3、数据库存储&#xff1a…

基于Java Web的网上房屋租售网站

内容摘要 本毕业设计题目为《基于Java Web的网上房屋租售网站》&#xff0c;是在信息化时代下充分利用互联网对传统房屋租售方式进行创新&#xff0c;在互联网上进行房屋租售突破了传统方式的局限性。对于房屋租售的当事人都提供了极大的便利。本稳针对了实际用户需求&#xf…