勉強 @ 最大長方形の面積 Largest Rectangle
Code Jam Africa 2011の予選C問題
に、縦方向にヒストグラム化してから最大長方形面積決定プログラム
で挑戦。
考え方としては
1.各マスから特定方向への連続可能数を決定。
2.ヒストグラムを横にみていく
目からうろこな方法。
#include <iostream> #include <fstream> #include <string> #include <vector> #include <algorithm> #include <stack> #include <cassert> #include <ctime> #define DEBUG_MODE (0) using namespace std; void debug_print(vector<int> &map, int width, int height) { #if DEBUG_MODE for(int h=0; h<height; ++h) { for(int w=0; w<width; ++w) { cout << map[h*width+w]; } cout << endl; } cout << endl; #endif } struct Rect { int start; int height; }; int get_largest_rectangle_in_histgram(const vector<int> &histgram) { int max_area = 0; stack<Rect> rects; for(int i=0; i<histgram.size(); ++i) { int height = histgram[i]; if (rects.empty()) { Rect r; r.start = i; r.height = height; rects.push(r); } else { int restart = i; while(!rects.empty() && rects.top().height >= height) { Rect pre = rects.top(); rects.pop(); restart = pre.start; int width = (i - restart); int area = pre.height * width; max_area = max(area, max_area); } Rect r; r.start = restart; r.height = height; rects.push(r); } } while(!rects.empty()) { Rect pre = rects.top(); rects.pop(); int width = (histgram.size() - pre.start); int area = pre.height * width; max_area = max(area, max_area); } return max_area; } vector<int> make_histgram_from_map(const vector<int> &map, int width, int height) { vector<int> histgram(height*width); for(int h=0; h<height; ++h) { for(int w=0; w<width; ++w) { if (0==h) { histgram[h*width+w] = map[w]; } else { int index = h*width+w; int index_up = index-width; histgram[h*width+w] = map[index] ? histgram[index_up]+1 : 0; } } } return histgram; } int get_max_rectangle_in_map(const vector<int> &map, int width, int height) { vector<int> histgram = make_histgram_from_map(map, width, height); debug_print(histgram, width, height); int max_area = 0; for(int h=0; h<height; ++h) { vector<int> line(width); for(int w=0; w<width; ++w) { line[w] = histgram[h*width+w]; } max_area = max(max_area, get_largest_rectangle_in_histgram(line)); } return max_area; } void input_a_case(ifstream &in, vector<int> &map, int &width, int &height) { in >> width; in >> height; for(int h=0; h<height; ++h) { for(int w=0; w<width; ++w) { char c; in >> c; bool ok = 'G'==c || 'S'==c; map.push_back(ok ? 1 : 0); } } } void solve_a_case(ifstream &in, int &area) { vector<int> map; int width = 0; int height = 0; input_a_case(in, map, width, height); area = get_max_rectangle_in_map(map, width, height); } void solve_all_cases(ifstream &in, ofstream &out) { int case_num = 0; in >> case_num; for(int i=0; i<case_num; i++) { int area = 0; solve_a_case(in, area); out << "Case #" << i+1 << ": "; out << area << endl; } } int main() { #if 0 ifstream in("in.txt"); ofstream out("out.txt"); #elif 0 ifstream in("C-small-practice.in"); ofstream out("C-small-practice.out"); #elif 1 ifstream in("C-large-practice.in"); ofstream out("C-large-practice.out"); #endif solve_all_cases(in, out); return 0; }
さすがに速い。
一列の終わりに高さ0の番兵仕込めば、もちっとスッキリするかな。
上方向に連結数を見てるから、下の列から面積決定して、暫定最大面積で枝狩りするのもいいかもしれない。
ただ、連結条件が上下左右に関連すると対応できない気がする。
柔軟性は力ずくが上か。。