[蓝桥杯]图形排版
图形排版
题目描述
小明需要在一篇文档中加入 NN 张图片,其中第 ii 张图片的宽度是 WiWi,高度是 HiHi。
假设纸张的宽度是 MM,小明使用的文档编辑工具会用以下方式对图片进行自动排版:
1. 该工具会按照图片顺序,在宽度 MM 以内,将尽可能多的图片排在一行。该行的高度是行内最高的图片的高度。例如在 M=10M=10 的纸张上依次打印 3x4, 2x2, 3x3 三张图片,则效果如下图所示,这一行高度为 4。(分割线以上为列标尺,分割线以下为排版区域;数字组成的矩形为第 xx 张图片占用的版面)
0123456789
----------
111
111 333
11122333
11122333
2.如果当前行剩余宽度大于 0,并且小于下一张图片,则下一张图片会按比例缩放到宽度为当前行剩余宽度(高度向上取整),然后放入当前行。例如再放入一张 4x9 的图片,由于剩余宽度是 2,这张图片会被压缩到 2x5,再被放入第一行的末尾。此时该行高度为 5:
0123456789
---------
44
111 44
111 33344
1112233344
1112233344
3.如果当前行剩余宽度为 0,该工具会从下一行开始继续对剩余的图片进行排版,直到所有图片都处理完毕。此时所有行的总高度和就是这 NN 张图片的排版高度。例如再放入 11x1, 5x5, 3x4 的图片后,效果如下图所示,总高度为 11:
0123456789
----------
44
111 44
111 33344
1112233344
1112233344
5555555555
66666
66666777
66666777
66666777
66666777
现在由于排版高度过高,图片的先后顺序也不能改变,小明只好从 NN 张图片中选择一张删除掉以降低总高度。他希望剩余 N−1N−1 张图片按原顺序的排版高度最低,你能求出最低高度是多少么?
输入描述
输入描述
第一行包含两个整数 M,NM,N,分别表示纸张宽度和图片的数量。
接下来 NN 行,每行 2 个整数 Wi,HiWi,Hi,表示第 ii 个图大小为 Wi×HiWi×Hi。
其中,1≤N≤1051≤N≤105,1≤M,Wi,Hi≤1001≤M,Wi,Hi≤100。
输出描述
一个整数,表示在删除掉某一张图片之后,排版高度最少能是多少。
输入输出样例
示例
输入
4 3
2 2
2 3
2 2
输出
2
运行限制
- 最大运行时间:2s
- 最大运行内存: 256M
总通过次数: 1602 | 总提交次数: 2957 | 通过率: 54.2%
难度: 困难 标签: 2017, 模拟, 省赛, 搜索
算法思路
本问题需要高效处理图片排版和删除优化,核心挑战在于:
- 排版规则复杂:涉及原始图片放置、缩放处理、行高计算
- 数据规模大:N ≤ 10⁵,需O(N)或O(N log N)解法
- 删除优化:需快速计算删除每张图片后的排版高度
采用双向预处理+模拟重新排版策略:
- 正向预处理:计算不删除时的行信息(起始位置、行高、下一行起点)
- 后缀数组预处理:g[i]表示从第i张图开始排版的总高度
- 枚举删除:对每个图片,重新排版所在行并拼接前后部分高度
算法演示
算法步骤
-
正向排版预处理
- 模拟整个序列排版,记录:
lines[]
:每行起始下标row_id[]
:每张图片所在行号nxt_line[]
:每行结束后的下一行起点line_height[]
:每行高度pre_total[]
:前行累计高度
- 模拟整个序列排版,记录:
-
后缀数组g[i]计算
- 从后向前动态规划:
g[N+1] = 0
- 对每个i从N到1:
- 模拟从i开始排版一行
- 计算行高和结束位置j
g[i] = 行高 + g[j]
- 从后向前动态规划:
-
枚举删除并计算
- 对每张图片k:
- 定位所在行x和起始位置L0
- 重新排版该行(跳过k):
- 遍历行内图片(跳过k)
- 根据剩余空间缩放图片
- 计算新行高和结束位置next_start
- 总高度 = 前行高度 + 新行高 + g[next_start]
- 取所有删除方案的最小高度
- 对每张图片k:
代码实现(C++)
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;const int MAXN = 100010;
int M, N;
int W[MAXN], H[MAXN];
int lines[MAXN], row_id[MAXN], nxt_line[MAXN], line_height[MAXN];
long long pre_total[MAXN], g[MAXN];// 缩放高度计算(向上取整)
inline int calc_scaled_height(int w, int h, int space) {return (h * space + w - 1) / w; // 整数运算避免浮点误差
}int main() {ios::sync_with_stdio(false);cin.tie(0);// 输入数据cin >> M >> N;for (int i = 1; i <= N; i++) {cin >> W[i] >> H[i];}// 正向排版预处理int row_count = 0;for (int i = 1; i <= N; ) {lines[++row_count] = i; // 记录行起始int current_width = 0;int max_h = 0;int j = i;// 排版当前行while (j <= N) {if (current_width + W[j] <= M) {current_width += W[j];max_h = max(max_h, H[j]);row_id[j] = row_count; // 记录图片所在行j++;} else {if (current_width < M) {int space = M - current_width;int scaled_h = calc_scaled_height(W[j], H[j], space);max_h = max(max_h, scaled_h);row_id[j] = row_count;j++; // 缩放后放入,指向下一张current_width = M; // 行满}break;}}line_height[row_count] = max_h;nxt_line[row_count] = j; // 下一行起始i = j; // 处理下一行}// 计算前行累计高度for (int i = 1; i <= row_count; i++) {pre_total[i] = pre_total[i - 1] + line_height[i];}// 后缀数组g[i]计算g[N + 1] = 0;for (int i = N; i >= 1; i--) {int current_width = 0;int max_h = 0;int j = i;// 从i开始排版一行while (j <= N) {if (current_width + W[j] <= M) {current_width += W[j];max_h = max(max_h, H[j]);j++;} else {if (current_width < M) {int space = M - current_width;int scaled_h = calc_scaled_height(W[j], H[j], space);max_h = max(max_h, scaled_h);j++; // 缩放后放入}break;}}g[i] = max_h + g[j]; // 当前行高 + 后续高度}// 枚举删除每张图片long long ans = 1e18;for (int k = 1; k <= N; k++) {int x = row_id[k]; // k所在行号int L0 = lines[x]; // 行起始位置long long prefix = pre_total[x - 1]; // 前行总高度// 重新排版当前行(跳过k)int current_width = 0;int max_h = 0;int j = L0;while (j <= N) {if (j == k) { // 跳过被删除图片j++;continue;}if (current_width + W[j] <= M) {current_width += W[j];max_h = max(max_h, H[j]);j++;} else {if (current_width < M) {int space = M - current_width;int scaled_h = calc_scaled_height(W[j], H[j], space);max_h = max(max_h, scaled_h);j++; // 缩放后放入}break;}}// 总高度 = 前行高度 + 新行高 + 后续高度long long total_height = prefix + max_h + g[j];ans = min(ans, total_height);}cout << ans << endl;return 0;
}
代码解析
-
数据结构
W[i]
,H[i]
:存储图片宽高lines[]
:记录每行起始图片下标row_id[i]
:图片i所在行号g[i]
:从第i张图开始排版的总高度
-
关键函数
calc_scaled_height()
:计算缩放高度(整数运算避免浮点误差)- 正向排版:模拟自动排版过程,记录行信息
- 后缀计算:动态规划生成g[]数组,加速后续查询
-
删除优化
- 枚举删除每张图片时,仅重新排版其所在行
- 利用预处理的
g[]
数组快速获取后续排版高度 - 时间复杂度:O(100N)(每行最多处理100张图)
实例验证
输入样例:
4 3
2 2
2 3
2 2
处理过程:
-
原始排版:
- 行1:图片1(2×2) + 图片2(2×3) → 行高=3
- 行2:图片3(2×2) → 行高=2
- 总高度=5
-
删除图片2:
- 行1:图片1(2×2) + 图片3(2×2) → 行高=2
- 总高度=2
-
算法计算:
pre_total[0]=0
(无前行)- 重新排版行1得
max_h=2
g[4]=0
(无后续图)- 总高度=0+2+0=2
注意事项
-
缩放计算:
- 使用整数运算:
(h * space + w - 1) / w
- 避免浮点误差和性能开销
- 使用整数运算:
-
边界处理:
- 行内无图片时,行高为0
- 最后一行结束时
j=N+1
-
性能优化:
- 每行最多处理100张图(因M≤100)
- 后缀数组g[]避免重复排版
-
内存优化:
- 数组大小设为100010(10⁵+10)
- 使用
long long
防溢出
相关文章:

[蓝桥杯]图形排版
图形排版 题目描述 小明需要在一篇文档中加入 NN 张图片,其中第 ii 张图片的宽度是 WiWi,高度是 HiHi。 假设纸张的宽度是 MM,小明使用的文档编辑工具会用以下方式对图片进行自动排版: 1. 该工具会按照图片顺序࿰…...

【Linux仓库】冯诺依曼体系结构与操作系统【进程·壹】
🌟 各位看官好,我是! 🌍 Linux Linux is not Unix ! 🚀 今天来学习冯诺依曼体系结构与操作系统。 👍 如果觉得这篇文章有帮助,欢迎您一键三连,分享给更多人哦࿰…...

CloudFront 加速详解:AWS CDN 怎么用?
让全球访问更快速稳定,深入解读 AWS 的内容分发网络 在上一篇中,我们介绍了 Amazon S3 对象存储,它非常适合托管静态资源,比如图片、视频、网页等。但你可能遇到过这样的问题: “我把网站静态文件部署到了 S3…...

《高级架构师》------- 考后感想
笔者来聊一下架构师考后的感想 复习备考 考前过了很多知识点,只是蜻蜓点水,没有起到复习的作用,即使考出来也不会,下次复习注意这个,复习到了,就记住,或者画出来,或者文件总结&…...

【iOS】YYModel源码解析
YYModel源码解析 文章目录 YYModel源码解析前言YYModel性能优势YYModel简介YYClassInfo解析YYClassIvarInfo && objc_ivarYYClassMethodInfo && objc_methodYYClassPropertyInfo && property_tYYClassInfo && objc_class YYClassInfo的初始化细…...

C++算法训练营 Day6 哈希表(1)
1.有效的字母异位词 LeetCode:242.有效的字母异位词 给定两个字符串s和t ,编写一个函数来判断t是否是s的字母异位词。 示例 1: 输入: s “anagram”, t “nagaram” 输出: true 示例 2: 输入: s “rat”, t “car” 输出: false 解题思路ÿ…...

【C语言编译与链接】--翻译环境和运行环境,预处理,编译,汇编,链接
目录 一.翻译环境和运行环境 二.翻译环境 2.1--预处理(预编译) 2.2--编译 2.2.1--词法分析 2.2.2--语法分析 2.2.3--语义分析 2.3--汇编 2.4--链接 三.运行环境 🔥个人主页:草莓熊Lotso的个人主页 🎬作者简介:C研发…...

【JavaEE】多线程
8.线程状态 根据 Java 的Thread.state包,线程一共有六种状态: NEWRUNNABLEBLOCKEDWAITINGTIMED_WAITINGTERMINATED 二、每种状态的含义 1. NEW(新建) 当使用new 关键字创建一个线程对象,但尚未调用其start() 方法时…...

【项目】在线OJ(负载均衡式)
目录 一、项目目标 二、开发环境 1.技术栈 2.开发环境 三、项目树 目录结构 功能逻辑 编写思路 四、编码 1.complie_server 服务功能 代码蓝图 开发编译功能 日志功能 编辑 测试编译模块 开发运行功能 设置运行限制 jsoncpp 编写CR 如何生成唯一文件名 …...

贪心算法应用:在线租赁问题详解
贪心算法应用:在线租赁问题详解 贪心算法是一种在每一步选择中都采取当前状态下最优的选择,从而希望导致结果是全局最优的算法策略。在线租赁问题(Greedy Algorithm for Online Rentals)是一个经典的贪心算法应用场景,下面我将从多个维度全面…...
torch.zeros()用法简介
torch.zeros()是PyTorch中用于创建全零张量的核心函数,其功能和使用方法如下: 1. 基本语法 torch.zeros(*size, outNone, dtypeNone, layouttorch.strided, deviceNone, requires_gradFalse)参数说明: *size:定义张量形状的…...

Prj10--8088单板机C语言8259测试(1)
1.原理图 2.Deepseek示例代码 #include <dos.h> #include <conio.h> #include <stdio.h>#define PIC1_CMD 0x400 // 命令端口 (A00) #define PIC1_DATA 0x401 // 数据端口 (A01)volatile int int_count 0; // 中断计数器 void interrupt (*old_isr)(…...

3步在小米13手机跑DeepSeek R1
大家好!我是羊仔,专注AI工具、智能体、编程。 一、从性能旗舰到AI主机 春节大扫除时,翻出尘封的小米13,这台曾以骁龙8 Gen2著称的性能小钢炮,如今正在执行更科幻的使命——本地运行DeepSeek R1。 想起两年前用它连续肝…...
数智管理学(十六)
二、分布式网络型结构的特点 分布式网络型结构是一种去中心化、扁平化和协作性的组织模式,与传统金字塔型结构形成鲜明对比。它通过赋予团队和个体更大的自主权,提升组织的灵活性和响应能力。 (一)节点化组织 1.模块化团队构成…...

注销微软账户
因为我的微软开发者账户丢失 Office E5 权限,因此需要注销。 若你需要注销微软账号,请点击下方超链接。 点击此处 注销之后仅剩一个正常的账户使用咯!!...

Ubuntu 服务器软件更新,以及常用软件安装 —— 一步一步配置 Ubuntu Server 的 NodeJS 服务器详细实录 3
前言 前面,我们已经 安装好了 Ubuntu 服务器系统,并且 配置好了 ssh 免密登录服务器 ,现在,我们要来进一步的设置服务器。 那么,本文,就是进行服务器的系统更新,以及常用软件的安装 调整 Ubu…...
Mysql常用知识3:Kafka和数据库优化
文章目录 一、分布式消息系统(Kafka相关问题5-10)5. Kafka如何保证消息不丢失?6. 项目中Kafka具体怎么使用的?7. 消息异常未发送成功怎么解决?8. 重试具体怎么做的,循环吗?9. 重试多次失败怎么办…...
Milvus单机模式安装和试用
1.安装ollama的package包; # install package pip install -U langchain-ollama2.我们直接使用ChatOllama实例化模型,并通过invoke进行调用; from langchain_ollama import ChatOllamallm ChatOllama(model"deepseek-r1") messa…...

飞牛NAS+Docker技术搭建个人博客站:公网远程部署实战指南
文章目录 前言1. Docker下载源设置2. Docker下载WordPress3. Docker部署Mysql数据库4. WordPress 参数设置5. 飞牛云安装Cpolar工具6. 固定Cpolar公网地址7. 修改WordPress配置文件8. 公网域名访问WordPress总结 前言 在数字化浪潮中,传统网站搭建方式正面临前所未…...

刷leetcode hot100返航必胜版--链表6/3
链表初始知识 链表种类:单链表,双链表,循环链表 链表初始化 struct ListNode{ int val; ListNode* next; ListNode(int x): val(x),next(nullptr) {} }; //初始化 ListNode* head new ListNode(5); 删除节点、添加…...

C# 序列化技术全面解析:原理、实现与应用场景
在软件开发中,数据持久化和网络通信是两个至关重要的环节。想象一下,当我们需要将一个复杂的对象保存到文件中,或者通过网络发送到另一台计算机时,如何有效地表示这个对象?这就是序列化技术要解决的问题。序列化&#…...
isp调试 blend模式指什么
isp调试 blend模式指什么 答案摘自豆包: 在图像信号处理(ISP,Image Signal Processor)调试中,Blend 模式(混合模式) 是指将不同处理阶段的图像数据或不同来源的图像信息按照特定规则进行叠加或…...

electron定时任务,打印内存占用情况
// 监听更新 function winUpdate(){// 每次执行完后重新设置定时器try {// 获取当前时间并格式化为易读的字符串const now new Date();const timeString now.toLocaleString();console.log(当前时间: ${timeString});// 记录内存使用情况(可选)const m…...

Gitee Wiki:以知识管理赋能 DevSecOps,推动关键领域软件自主演进
关键领域软件研发中的知识管理困境 传统文档管理模式问题显著 关键领域软件研发领域,传统文档管理模式问题显著:文档存储无系统,查找困难,降低效率;更新不及时,与实际脱节,误导开发࿱…...

学习STC51单片机24(芯片为STC89C52RCRC)
每日一言 把 “我不行” 换成 “我试试”,你会发现一片新的天地。 那关于优化 白盒测试 我们之前不是通过这个接线方式可以看到返回到信息嘛因为安信可的特性就是返回Esp8266的反馈,可以看到代码死在哪里了,导致连接不上,因为我们…...

LabVIEW基于 DataSocket从 OPC 服务器读取数据
LabVIEW 中基于 DataSocket 函数从 OPC 服务器读取数据的功能,为工业自动化等场景下的数据交互提供了解决方案。通过特定函数实现 URL 指定、连接建立与管理、数据读取,相比传统 Socket 通信和 RESTful API ,在 OPC 服务器数据交互场景有适配…...

阿里云无影云桌面深度测评
阿里云无影桌面深度测评:解锁云端工作“新范式”的“未来之钥”! 在数字化浪潮席卷全球的2025年,远程办公与混合办公已不再是权宜之计,而是职场不可逆转的新常态。然而,如何确保员工无论身在何处,都能拥有…...
【208】VS2022 C++ 32位整数和unsigned char数组之间互相转换
一、场景 在实际应用中,特别是在数据传输的时候,需要读取unsigned char数组,再转换成 32 位整数;或者把 32 位整数转换成 unsigned char数组进行写入。比如对接西门子PLC的 snap7 就是这样。32 位整数分成有符号的无符号的&#…...
数据库技术
InnoDB是什么?MySQL 和 InnoDB的关系是什么? InnoDB是MySQL数据库系统中最重要且默认的存储引擎。MySQL采用插件式存储引擎架构,作为数据库管理系统本身不直接处理数据存储,而是通过存储引擎接口与InnoDB等引擎交互。InnoDB作为M…...

深入浅出:Oracle 数据库 SQL 执行计划查看详解(1)——基础概念与查看方式
背景 在当今的软件开发领域,尽管主流开发模式往往倾向于采用单表模式,力图尽可能地减少表之间的连接操作,以期达到提高数据处理效率、简化应用逻辑等目的。然而,对于那些已经上线运行多年的运维老系统而言,它们内部往…...