单调栈:平面上N个底部对齐紧靠在一起的矩形,宽高是任意整数,求其最大内切矩形的面积(版本2)

本文介绍一种求解多个矩形盒子排列组合下最大内切矩形面积的算法,利用单调栈原理处理不同宽度和高度的矩形,提供C++实现代码并举例说明。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

注意,RangeItem里有了width的状态定义,实际上不需要2次遍历,新代码如下:

include <iostream>
#include <cassert>
#include <vector>

struct Box {
    int width;
    int height;
};

struct RangeItem {
    int start_index=-1;
    int end_index=-1;
    int width=-1;//处理高度递降的情况是必需的
    int height=-1;
    long area=-1;
    RangeItem(){}
    RangeItem(int s, int e, int w, int h, long a):start_index(s),end_index(e),width(w), height(h),area(a){}
    friend std::ostream& operator<<(std::ostream& out, const RangeItem& ri){
        out<<"RangeItem ["<<ri.start_index<<", "<<ri.end_index<<"] width="<<ri.width<<" height="<<ri.height<<" area="<<ri.area;
        return out;
    }
};

/**
 平面上N个矩形盒子底部水平对齐紧靠在一起放在地上,求其最大内切矩形的面积
 提示:利用单调栈,对于盒子宽度相同(不妨假设=1)的情况,是一道ACM/ICPC题目,也是一道Google面试题
 这里处理的是每个矩形的宽度高度任意的情况(面积累加使用long类型,没有考虑溢出的情况)
 */
long solve(Box boxes[], int N) {
    assert(N>=0);
    if (N==1)
        return boxes[0].width * (long)boxes[0].height;
    
    RangeItem max_ri_l2r(-1,-1,-1,-1,-1); //invalid
    std::vector<RangeItem> L;
        //stack不支持for-range语法,只好改用vector了
    //L中存储的项的height是按push_back顺序递增的
    for(int i=0; i<N; ++i) {
        const Box& box = boxes[i];
        int width = box.width;
        int height = box.height;
        long area = box.width * (long)height;
        std::cout<<"Box["<<i<<"] width="<<width<<" height="<<height<<" area="<<area<<std::endl;
        //反向扫描栈;
        RangeItem max_popped_ri;
        //注意:我们可以把水平线一排box视作一系列高度单调上升的range项,每次遇到一个高度下降的则产生一个max_popped_ri
        long max_popped_area=-1;
        int max_popped_width=-1;
        int max_popped_width_start_index=-1;
        if (L.size()>0) {
            RangeItem& ri = L.back();
            while (ri.height>height && L.size()>0) {
                //注意!这里对简单的输入4,3,2,1(width=1)的情况会产生遗漏,解决方法就是从右往左再来一遍!
                if (ri.area>max_popped_area) {
                    max_popped_area = ri.area;
                    max_popped_ri = ri;
                }
                //在丢弃单调栈中height大于当前box height的项之前,也许需要重新插入新的项:
                //例子:L中已有高度为3,4的项,其宽度分别为3,2,面积为9,8
                //	   这个时候下一个box的高度为2,但是其宽度=100(!)
                //也就是说,需要记住pop出去的3,4两项的最大宽度,实际上就是最后一个pop_back出去的
                max_popped_width = ri.width;
                max_popped_width_start_index = ri.start_index;
                L.pop_back();//discard;
                if(L.size()==0)
                    break;
            }
        }
        if (max_popped_area>0) {
            if (max_popped_ri.area > max_ri_l2r.area) {
                max_ri_l2r = max_popped_ri;
                std::cout<<"[L2R]Set current max_max_popped_ri="<<max_ri_l2r<<std::endl;
            }
        }
        //现在当前box的height>=单调栈中所有的RangeItem的height,扩展栈中所有ri的end_index和area
        for(RangeItem& ri2 : L) {
            ri2.end_index = i;
            ri2.width += width;
            ri2.area += ri2.height * (long)width;
        }
        //如果当前box的height大于单调栈S中的top项,则它自己也扩展为一个新的RangeItem/start_index;
        if (L.empty() || box.height > L.back().height) {
        	RangeItem new_ri(i, i, width, height, area);
        	if (max_popped_width>0) {
        		new_ri.width += max_popped_width;
        		new_ri.start_index = max_popped_width_start_index;
        		new_ri.area += max_popped_width * height;
        	}
            L.push_back(new_ri);
        }
    }
    //OK, 现在再次遍历一遍单调栈L(注意,当原始输入的boxes的height正好是全局递增的情况,L<span style="font-family: Arial, Helvetica, sans-serif;">中元素最多</span><span style="font-family: Arial, Helvetica, sans-serif;">),取出面积最大的
</span>    //当然也可以不必这么做,而是在前面再增加一个额外的状态变量以记住L中area最大的,不过这种冗余状态变量实际上会导致代码可读性变差
    {
        RangeItem& max_ri = max_ri_l2r; //rename
        for(RangeItem& ri : L) {
            if (ri.area > max_ri.area)
                max_ri = ri;
        }
        max_ri_l2r = max_ri;
    }
    
    RangeItem max_ri = max_ri_l2r;//rename;
    
    std::cout<<"Max Range: ["<<max_ri.start_index<<", "<<max_ri.end_index<<"]"
    <<" width="<<max_ri.width
    <<" height="<<max_ri.height
    <<" area="<<max_ri.area
    <<"\n"<<std::endl;
    return max_ri.area;
}

int main(int argc, char** argv){
    //test:
    static Box boxes1[] = {{1,1}, {1,2}, {1,3}, {1,4}};
    solve(boxes1, 4);//6
    static Box boxes2[] = {{1,1}, {10,2}, {5,3}, {1,4}};
    solve(boxes2, 4);//32
    static Box boxes3[] = {{1,4}, {1,3}, {1,2}, {1,1}};
    solve(boxes3, 4);//6
    static Box boxes4[] = {{2,3}, {2,3}, {2,3}, {2,3}};
    solve(boxes4, 4);//24
    //TODO:  需要更多的测试数据
    return 0;
}

哼哼,鸟问题终于把它想通了。只是算法复杂度不好分析,到底是O(N)呢?还是O(N*lgN)?注意,当width都=1的情况下为O(N)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值