注意,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)