[蓝桥杯]图形排版

news2025/7/23 6:50:40

图形排版

题目描述

小明需要在一篇文档中加入 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, 模拟, 省赛, 搜索

算法思路

本问题需要高效处理图片排版和删除优化,核心挑战在于:

  1. ​排版规则复杂​​:涉及原始图片放置、缩放处理、行高计算
  2. ​数据规模大​​:N ≤ 10⁵,需O(N)或O(N log N)解法
  3. ​删除优化​​:需快速计算删除每张图片后的排版高度

采用​​双向预处理+模拟重新排版​​策略:

  1. ​正向预处理​​:计算不删除时的行信息(起始位置、行高、下一行起点)
  2. ​后缀数组预处理​​:g[i]表示从第i张图开始排版的总高度
  3. ​枚举删除​​:对每个图片,重新排版所在行并拼接前后部分高度

 算法演示

算法步骤

  1. ​正向排版预处理​

    • 模拟整个序列排版,记录:
      • lines[]:每行起始下标
      • row_id[]:每张图片所在行号
      • nxt_line[]:每行结束后的下一行起点
      • line_height[]:每行高度
      • pre_total[]:前行累计高度
  2. ​后缀数组g[i]计算​

    • 从后向前动态规划:
      • g[N+1] = 0
      • 对每个i从N到1:
        • 模拟从i开始排版一行
        • 计算行高和结束位置j
        • g[i] = 行高 + g[j]
  3. ​枚举删除并计算​

    • 对每张图片k:
      • 定位所在行x和起始位置L0
      • ​重新排版该行(跳过k)​​:
        • 遍历行内图片(跳过k)
        • 根据剩余空间缩放图片
        • 计算新行高和结束位置next_start
      • 总高度 = 前行高度 + 新行高 + g[next_start]
    • 取所有删除方案的最小高度

代码实现(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;
}

代码解析

  1. ​数据结构​

    • W[i]H[i]:存储图片宽高
    • lines[]:记录每行起始图片下标
    • row_id[i]:图片i所在行号
    • g[i]:从第i张图开始排版的总高度
  2. ​关键函数​

    • calc_scaled_height():计算缩放高度(整数运算避免浮点误差)
    • 正向排版:模拟自动排版过程,记录行信息
    • 后缀计算:动态规划生成g[]数组,加速后续查询
  3. ​删除优化​

    • 枚举删除每张图片时,仅重新排版其所在行
    • 利用预处理的g[]数组快速获取后续排版高度
    • 时间复杂度:O(100N)(每行最多处理100张图)

实例验证

输入样例:

4 3
2 2
2 3
2 2

处理过程:

  1. ​原始排版​​:

    • 行1:图片1(2×2) + 图片2(2×3) → 行高=3
    • 行2:图片3(2×2) → 行高=2
    • 总高度=5
  2. ​删除图片2​​:

    • 行1:图片1(2×2) + 图片3(2×2) → 行高=2
    • 总高度=2
  3. ​算法计算​​:

    • pre_total[0]=0(无前行)
    • 重新排版行1得max_h=2
    • g[4]=0(无后续图)
    • 总高度=0+2+0=2

注意事项

  1. ​缩放计算​​:

    • 使用整数运算:(h * space + w - 1) / w
    • 避免浮点误差和性能开销
  2. ​边界处理​​:

    • 行内无图片时,行高为0
    • 最后一行结束时j=N+1
  3. ​性能优化​​:

    • 每行最多处理100张图(因M≤100)
    • 后缀数组g[]避免重复排版
  4. ​内存优化​​:

    • 数组大小设为100010(10⁵+10)
    • 使用long long防溢出

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

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

相关文章

【Linux仓库】冯诺依曼体系结构与操作系统【进程·壹】

&#x1f31f; 各位看官好&#xff0c;我是&#xff01; &#x1f30d; Linux Linux is not Unix &#xff01; &#x1f680; 今天来学习冯诺依曼体系结构与操作系统。 &#x1f44d; 如果觉得这篇文章有帮助&#xff0c;欢迎您一键三连&#xff0c;分享给更多人哦&#xff0…

CloudFront 加速详解:AWS CDN 怎么用?

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

《高级架构师》------- 考后感想

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

【iOS】YYModel源码解析

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

C++算法训练营 Day6 哈希表(1)

1.有效的字母异位词 LeetCode&#xff1a;242.有效的字母异位词 给定两个字符串s和t &#xff0c;编写一个函数来判断t是否是s的字母异位词。 示例 1: 输入: s “anagram”, t “nagaram” 输出: true 示例 2: 输入: s “rat”, t “car” 输出: false 解题思路&#xff…

【C语言编译与链接】--翻译环境和运行环境,预处理,编译,汇编,链接

目录 一.翻译环境和运行环境 二.翻译环境 2.1--预处理(预编译) 2.2--编译 2.2.1--词法分析 2.2.2--语法分析 2.2.3--语义分析 2.3--汇编 2.4--链接 三.运行环境 &#x1f525;个人主页&#xff1a;草莓熊Lotso的个人主页 &#x1f3ac;作者简介&#xff1a;C研发…

【JavaEE】多线程

8.线程状态 根据 Java 的Thread.state包&#xff0c;线程一共有六种状态&#xff1a; NEWRUNNABLEBLOCKEDWAITINGTIMED_WAITINGTERMINATED 二、每种状态的含义 1. NEW&#xff08;新建&#xff09; 当使用new 关键字创建一个线程对象&#xff0c;但尚未调用其start() 方法时…

【项目】在线OJ(负载均衡式)

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

贪心算法应用:在线租赁问题详解

贪心算法应用&#xff1a;在线租赁问题详解 贪心算法是一种在每一步选择中都采取当前状态下最优的选择&#xff0c;从而希望导致结果是全局最优的算法策略。在线租赁问题(Greedy Algorithm for Online Rentals)是一个经典的贪心算法应用场景&#xff0c;下面我将从多个维度全面…

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

大家好&#xff01;我是羊仔&#xff0c;专注AI工具、智能体、编程。 一、从性能旗舰到AI主机 春节大扫除时&#xff0c;翻出尘封的小米13&#xff0c;这台曾以骁龙8 Gen2著称的性能小钢炮&#xff0c;如今正在执行更科幻的使命——本地运行DeepSeek R1。 想起两年前用它连续肝…

注销微软账户

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

Ubuntu 服务器软件更新,以及常用软件安装 —— 一步一步配置 Ubuntu Server 的 NodeJS 服务器详细实录 3

前言 前面&#xff0c;我们已经 安装好了 Ubuntu 服务器系统&#xff0c;并且 配置好了 ssh 免密登录服务器 &#xff0c;现在&#xff0c;我们要来进一步的设置服务器。 那么&#xff0c;本文&#xff0c;就是进行服务器的系统更新&#xff0c;以及常用软件的安装 调整 Ubu…

飞牛NAS+Docker技术搭建个人博客站:公网远程部署实战指南

文章目录 前言1. Docker下载源设置2. Docker下载WordPress3. Docker部署Mysql数据库4. WordPress 参数设置5. 飞牛云安装Cpolar工具6. 固定Cpolar公网地址7. 修改WordPress配置文件8. 公网域名访问WordPress总结 前言 在数字化浪潮中&#xff0c;传统网站搭建方式正面临前所未…

刷leetcode hot100返航必胜版--链表6/3

链表初始知识 链表种类&#xff1a;单链表&#xff0c;双链表&#xff0c;循环链表 链表初始化 struct ListNode{ int val; ListNode* next; ListNode(int x): val&#xff08;x&#xff09;,next(nullptr) {} }; //初始化 ListNode* head new ListNode(5); 删除节点、添加…

C# 序列化技术全面解析:原理、实现与应用场景

在软件开发中&#xff0c;数据持久化和网络通信是两个至关重要的环节。想象一下&#xff0c;当我们需要将一个复杂的对象保存到文件中&#xff0c;或者通过网络发送到另一台计算机时&#xff0c;如何有效地表示这个对象&#xff1f;这就是序列化技术要解决的问题。序列化&#…

electron定时任务,打印内存占用情况

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

Gitee Wiki:以知识管理赋能 DevSecOps,推动关键领域软件自主演进

关键领域软件研发中的知识管理困境 传统文档管理模式问题显著 关键领域软件研发领域&#xff0c;传统文档管理模式问题显著&#xff1a;文档存储无系统&#xff0c;查找困难&#xff0c;降低效率&#xff1b;更新不及时&#xff0c;与实际脱节&#xff0c;误导开发&#xff1…

学习STC51单片机24(芯片为STC89C52RCRC)

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

LabVIEW基于 DataSocket从 OPC 服务器读取数据

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