動的計画法練習 その2
最大正方形 | 動的計画法 | Aizu Online Judge
* 隣接する三点の dp 結果だけで現在の地点を含む最大正方形の幅がわかってしまう。
* ALGORITHM NOTE 最大正方形の面積 正方形探索を参照した。
class max_square { public: // O(H*W) void solve(void) { int H, W; cin>>H>>W; // dp[i][j] := [0,i)x[0,j) 区間内に含まれる最大正方形の幅 // // X // ABBBX A : 4x4 // XABBCC B : 3x3 // ABBCC C : 2x2 // AAAA? // // X // ABBBX ? = min{2,3,4} // XABBCC // ABB32 // AAA4? // // dp[i][j] は隣接する (i,j-1) (i-1,j) (i-1,j-1) での計算結果からわかる vector<vector<int>> dp(H+1, vector<int>(W+1,0)); // dp の定義より左端一列と一番上一行は 0 となる(ため index は 1 から開始) FOR(i, 1, H+1) FOR(j, 1, W+1) { int t; cin>>t; if (t == 0) dp[i][j] = 1; } int maxWidth = 0; FOR(i, 1, H+1) FOR(j, 1, W+1) { if (!dp[i][j]) continue; dp[i][j] = min({dp[i-1][j], dp[i][j-1], dp[i-1][j-1]})+1; maxWidth = max(maxWidth, dp[i][j]); } cout<<maxWidth*maxWidth<<endl; } };
最大長方形 | 動的計画法 | Aizu Online Judge
* 上の問題の長方形板。縦方向の長さを計算しておいて largest rectangle in histogram に帰着させる。
* ALGORITHM NOTE 長方形探索: 最大長方形の面積 Largest Rectangle を参照にした。
class max_rectangle { public: // // Largest Rectangle in Histogram のアルゴリズムを応用する // void solve(void) { int H, W; cin>>H>>W; // 上に向かってきれいなタイルが連続する長さを格納する // . X . X 1 0 1 0 // . . . . -> 2 1 2 1 // X . . . 0 2 3 2 vector<vector<int>> seq(H, vector<int>(W,0)); REP(i, H) REP(j, W) { int t; cin>>t; if (i == 0) { if (t == 0) seq[i][j] = 1; } else { if (t == 0) seq[i][j] = seq[i-1][j]+1; } } int maxArea = 0; // 各行を底面として largest rectangle in histogram を解く // // . . X . . // . X . X . . // . . . . --> . . . . // X . . . // REP(i, H) maxArea = max(maxArea, (int)largestRectInHist(seq[i])); cout<<maxArea<<endl; } };
SPOJ.com - Problem HISTOGRA
* 上の問題で使ったアルゴリズム
* スタックにつむときに 2 パターンあるのがポイント
class largest_rectangle_in_histogram { public: // 長方形の始点と終点を回る、素朴な2重ループでは O(N^2) で TLE してしまう。 // stack を使った O(N) の解法を考える ll max_rectangle(const vector<ll> &_hist) { vector<ll> hist(_hist); stack<pair<int,ll>> st; ll maxArea = 0; // 最後のグラフも同じように扱いたいので、末尾に高さ 0 のダミーグラフをいれておく hist.push_back(0); REP(i, hist.size()) { if (st.empty()) st.emplace(i, hist[i]); else if (st.top().second == hist[i]) // 同じ高さが続くなら次へ continue; else { int prev = i; while ( st.size() && hist[i] < st.top().second ) { int l; ll h; tie(l,h) = st.top(); st.pop(); prev = l; maxArea = max(maxArea, (i-l)*h); } // // 上りで // // | // +-+ // |/ // +-+/ // |A B C // // B から長方形始まるケースとして (B,hist[B]) を登録するケース // // +--+ // | +-+ // +-+ | // |......+-+ //-+ | // A BC D E // E からさかのぼって (A,hist[E]) を登録するケース // st.emplace(prev, hist[i]); } } return maxArea; } void solve(void) { while (true) { int N; cin>>N; if (N == 0) break; vector<ll> hist; REP(i, N) { ll h; cin>>h; hist.push_back(h); } cout<<max_rectangle(hist)<<endl; } } };
なんか最後の方は動的計画法っぽくないな。