蓝桥杯-[图形排版] 详解

图形排版

小明需要在一篇文档中加入 N 张图片,其中第张图片的宽度是Wi,高度是 Hi。
假设纸张的宽度是M,小明使用的文档编辑工具会用以下方式对图片进行自动排版:

  1. 该工具会按照图片顺序,在宽度 M以内,将尽可能多的图片排
    在一行。该行的高度是行内最高的图片的高度。例如在 M10
    的纸张上依次打印 3x4,2x2,3x3 三张图片,则效果如下图所示,这
    行高度为4。(分割线以上为列标尺,分割线以下为排版区域;数
    字组成的矩形为第a 张图片占用的版面
    111
    111-----333
    111 22 333
    111 22 333

  2. 如果当前行剩余宽度大于 0,并且小于下一张图片,则下一张图
    片会按比例缩放到宽度为当前行剩余宽度(高度向上取整),然后放
    入当前行。例如再放入一张 4x9 的图片,由于剩余宽度是2,这张
    图片会被压缩到2x5,再被放入第一行的末尾。此时该行高度为6
    ---------------44
    111-----------44
    111-----333 44
    111 22 333 44
    111 22 333 44

  3. 如果当前行剩余宽度为 0,该工具会从下一行开始继续对剩余的
    图片进行排版,直到所有图片都处理完毕。此时所有行的总高度和
    就是这N张图片的排版高度。例如再放入 11x1,5x5,3x4的图片
    后,效果如下图所示,总高度为 11:
    ----------------44
    111-----------44
    111---- 333 44
    111 22 333 44
    111 22 333 44
    5 555 555 555
    6 666 6
    6 666 6 7 7 7
    6 666 6 7 7 7
    6 666 6 7 7 7
    6 666 6 7 7 7
    现在由于排版高度过高,图片的先后顺序也不能改变,小明只好从
    V 张图片中选择一张删除掉以降低总高度。他希望剩余N1张
    图片按原顺序的排版高度最低,你能求出最低高度是多少么?

输入描述

第一行包含两个整数 MN,分别表示纸张宽度和图片的数量
接下来N行,每行2个整数W,H,表示第个图大小为
Wix Hi。
其中,1< N< 105,1<M,Wi, Hi< 100

输出描述

个整数,
表示在删除掉某一张图片之后,排版高度最少能是多

输入

4 3
2 2
2 3
2 2

输出

2

运行限制

  • 最大运行时间: 2s
  • 最大运行内存:256M

题目意思

将N张图片进行排版后,从中抽取一张出来,使得总高度最小,并且图片的顺序不变,例如:12345
67----
从中抽取3后,排序变为
12456
7-------

暴力 O(n2)

将每一张图片被删除后排版的高度列举出来,选出最小的高度。
但是只能过部分测试用例。

package lanqiao;

import java.util.Scanner;

public class Mar15 {
    static int n = 100000+10;
    static int M, N;
    static int[] W = new int[n];
    static int[] H = new int[n];
    
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        M = sc.nextInt();
        N = sc.nextInt();
        for(int i = 0; i < N; i++) {
            W[i] = sc.nextInt();
            H[i] = sc.nextInt();
        }
        int ans = Integer.MAX_VALUE; // 将一个int型最大值赋值给ans,用于后面选出最小高度
        for(int i = 0; i < N; i++) { // 依次遍历每张图片,被遍历到的图片即为被删除的图片
            int h = cal(i);
//            System.out.println("第"+i+"个:"+ h);
            ans = Math.min(h,ans);
        }

        System.out.println(ans);
    }
    
	/*
	* cal方法用于将图片进行排版,返回排版后的高度
	* 接收参数i,表示第i个图形,在排版的过程中遇到第i个图形就跳过,相当于将第i个图形删除
	*/
    static int cal(int i) {
        int sum = 0, j = 0;
        while(j < N) {
            int w = 0, h = 0, temp;
            while(w < M && j < N) {
                if(j == i) {
                    j++;
                    continue; // 遇到第i个图形就跳过
                }
                if(w + W[j] > M) {
                    temp = ( (H[j] * (M - w) - 1)/ W[j] )+ 1; // 向上取整,(x-1/y) + 1
                } else {
                    temp = H[j];
                }
                h = Math.max(h, temp);
                w += W[j];
                j++;
            }
//            System.out.print(h + "  ");
            sum += h;
        }
        return sum;
    }

}

测试结果:
在这里插入图片描述

优化后

排版后的图片,从中抽取一张删除,可以发现可以将图片分成三部分:删除图片所在行、删除图片所在行的上面部分,删除图片所在行的下面部分
如下图:左侧,绿色代表需要删除的图片,将图片删除后如右侧所示
紫色部分不动,蓝色部分一些图片往红色一行补充
在这里插入图片描述 在这里插入图片描述
排版的高度等于上面行高度+删除图片所在行高度+下面行高度
因此我们依次遍历图片Pi ,用变量pre_h保存紫色部分的高度,用cal()计算删除图片所在行的高度+下面行的高度,用h保存,这样ans就取min(pre_h + h, ans),ans就是所求的最小高度

一、cal(int w, int h, int i)

我们需要一个方法,接收参数i,返回i所在行的高度+下面行的高度。
参数i作为起始图片,找到图片j(i <= j < N),图片j是i所在行最后一个图片,或者是最后一个图片(N-1),那么 j+1就是下一行的第一个图片,或者是N,假设用h存储i所在行的高度最大值,此外有个t[]数组存储着第k个(0<= k <= N)图片为开头的图片排版后的高度,那么我们就可以return h + t [j+1]

只有参数i还不行,注意看上面左侧图,假设删除图片为Pi ,i左边红色部分宽度需要用temp_w保存,左边红色部分图片中高度最大值需要用temp_h保存,并传入cal(),这样我们只需要计算第i+1个图片,i左边的图片用temp_w, temp_h来约束。

二、t[]

t数组存储的是第k个(0<= k <= N)图片为开头的图片排版后的高度
如图:
在这里插入图片描述
第一行以第1张图片开头,所以排版后的高度是t[0],第二行以第5张图片开头,所以从第二行以第5张图片为开头排版后的高度是t[4],t[0] - t[4]可以得到第一行的高度
如图:
在这里插入图片描述
删除第1张图片后,以第2张图片为开头的高度为t[1],以第6张图片为开头的高度为t[5]。

t数组初始化从后往前遍历每张图片
如图,从图片排版后面部分截取了几张图片1~8:
在这里插入图片描述
t[N]为0,从N-1(最后一张图片)开始。若要计算t[1],则计算1所在行的高度h,以及下面行的高度t[5](因为下面是以编号5这张图片作为开头的)
P S : 数组索引随便取的,看得懂就行 \textcolor{red}{PS:数组索引随便取的,看得懂就行} PS:数组索引随便取的,看得懂就行

代码如下:

package lanqiao;

import java.util.Scanner;

/**
 * 图形转换,自我总结
 * @author yu
 * @date 23/03/2024 10:19 AM
 */
public class March23 {
    static int M, N;
    static int NN = (int) Math.pow(10,5) + 10;
    static int[] W = new int[NN];
    static int[] H = new int[NN];
    static int[] t = new int[NN];
    public static void main(String[] args) {
        int h, temp_w = 0, temp_h = 0, ans = Integer.MAX_VALUE, pre_h = 0;

        Scanner sc = new Scanner(System.in);
        M = sc.nextInt();
        N = sc.nextInt();
        for(int i = 0; i < N; i++){
            W[i] = sc.nextInt();
            H[i] = sc.nextInt();
        }
        for(int i = N-1; i >= 0; i--) {
            t[i] = cal(0,0, i); //将每个图片作为第一行第一张时的总高度存入t数组
        }

        for(int i = 0; i < N; i++) {
            h = cal(temp_w, temp_h, i+1);
            ans = Math.min(pre_h + h, ans);
            if(temp_w + W[i] > M) {
                int hh = (H[i] * (M - temp_w) - 1 )/ W[i] + 1; //压缩高度并向上取整
                temp_h = Math.max(temp_h, hh);
            } else {
                temp_h = Math.max(temp_h, H[i]);
            }
            temp_w += W[i];
            if(temp_w >= M) { // 宽度已经超过M了,该换到下一行,temp_w 和 temp_h 该置零
                pre_h += temp_h;
                temp_w = 0;
                temp_h = 0;
            }
        }
        System.out.println(ans);
        sc.close();
    }

    public static int cal(int w, int h, int i) {
        int j = i, hh = h, ww = w;
        while(j < N && w < M) {
            if(w + W[j] > M) {
                h = (H[j]*(M-w) - 1) / W[j] + 1;// 向上取整
            } else {
                h = H[j];
            }
            w += W[j];
            hh = Math.max(h,hh);
            j++;
        }
        return hh + t[j];  //当前行高度加上剩余行的高度
    }
}

在这里插入图片描述
向上取整公式记得加括号,(x-1) / y + 1,原本是 x − 1 y + 1 \frac{x-1}{y} +1 yx1+1 去掉括号后就变成了 x − 1 y + 1 x -\frac{1}{y} +1 xy1+1

最后

写这篇文章主要是来捋一下自己的思路,看了别人通过的代码后自己试着手敲出来。
虽然写的像是一坨,但如果能帮到你的话,帮忙点个赞吧,嘿嘿

  • 16
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值