srupのメモ帳

競プロで解いた問題や勉強したことを記録していくメモ帳

aoj 1176 Planning Rolling Blackouts

問題

問題概要

長方形が与えられ, そのますに値が入っている. 長方形を分割してくが, 分割方法は区切られた長方形を一つ選び, それを縦か横どちらに切って分ける.
分割できるのは, 分割後に任意のグループを一つ選び, 抑制需要(残ったグループのますの値の合計値)が供給量を超えてはならない. さらに分割数を最大にし, さらに予備力(供給力と最大抑制需要の差)を最大にしなければならない.

解法

まず長方形の角を指定したら, その中のマスの値の合計値がO(1)で求まるように, 二次元累積和用いて計算できるようにしておく.
あとは, dp[y1][x1][y2][x2] := 左上(y1, x1), 右下(y2, x2)の長方形を考えた時の, グループの分け方の最大数と最大の予備力
という状態を考えてdpしていく.
ある長方形を考えた時に, 分割する方法は縦にどこかで切るか, 横にどこかで切るか(またはこれ以上きれない)しかないのでこれらをすべて試せば良い. これをdfsの中で行っている. 値の更新方法は長方形を切り, 作られた2つの長方形それぞれのグループの分け方と予備力の最大値を用いて, 更新することができる. グループの分け方は2つの長方形の値を単に足せばよくて, 予備力は供給力と最大抑制需要の差のことなので, 2つの長方形の値の最小値をつかえばよい.

ミス

長方形を分割していくのは再起関数で書くと楽ですね.

コード

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef vector<int> vint;
typedef pair<int,int> pint;
typedef vector<pint> vpint;
#define rep(i,n) for(int i=0;i<(n);i++)
#define REP(i,n) for(int i=n-1;i>=(0);i--)
#define reps(i,f,n) for(int i=(f);i<(n);i++)
#define each(it,v) for(__typeof((v).begin()) it=(v).begin();it!=(v).end();it++)
#define all(v) (v).begin(),(v).end()
#define eall(v) unique(all(v), v.end())
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define chmax(a, b) a = max(a, b);
#define chmin(a, b) a = min(a, b);
const int MOD = 1e9 + 7;
const int INF = 1e9;
const ll INFF = 1e18;

int h, w, s;
int U[35][35];
int allsum = 0;
int sum[35][35];
// dp[y1][x1][y2][x2] := 左上(y1, x1), 右下(y2, x2)の長方形を考えた時の, 
// グループの分け方の最大数, 最大の予備力
pair<int, int> dp[35][35][35][35];

void sum2D(int h, int w) {
    rep(y, h + 1)rep(x, w + 1) sum[y][x] = 0;
    rep(y, h)rep(x, w) sum[y + 1][x + 1] = U[y][x]; // まずは埋め込む
    rep(y, h + 1)rep(x, w) sum[y][x + 1] += sum[y][x]; // 横
    rep(y, h)rep(x, w + 1) sum[y + 1][x] += sum[y][x]; // 縦
}
int calcSum(int y1, int x1, int y2, int x2) {
    return sum[y2 + 1][x2 + 1] - sum[y2 + 1][x1] - sum[y1][x2 + 1] + sum[y1][x1];
}

pair<int, int> dfs(int y1, int x1, int y2, int x2) {
    if(dp[y1][x1][y2][x2] != make_pair(0, INF)) return dp[y1][x1][y2][x2];
    
    auto ret = make_pair(1, s - (allsum - calcSum(y1, x1, y2, x2))); // これ以上分割できない場合の値

    for (int i = x1; i < x2; ++i){
        if(s - (allsum - calcSum(y1, x1, y2, i)) < 0) continue;
        if(s - (allsum - calcSum(y1, i + 1, y2, x2)) < 0) continue;
        auto ret1 = dfs(y1, x1, y2, i);
        auto ret2 = dfs(y1, i + 1, y2, x2);
        chmax(ret, make_pair(ret1.fi + ret2.fi, min(ret1.se, ret2.se)));
    }

    for (int i = y1; i < y2; ++i){
        if(s - (allsum - calcSum(y1, x1, i, x2)) < 0) continue;
        if(s - (allsum - calcSum(i + 1, x1, y2, x2)) < 0) continue;
        auto ret1 = dfs(y1, x1, i, x2);
        auto ret2 = dfs(i + 1, x1, y2, x2);
        chmax(ret, make_pair(ret1.fi + ret2.fi, min(ret1.se, ret2.se)));
    }
    return dp[y1][x1][y2][x2] = ret;
}

int main(void) {
    while(1) {
        scanf("%d %d %d", &h, &w, &s);

        if(h == 0 && w == 0 && s == 0) break;
        rep(i, h)rep(j, w) scanf("%d", &U[i][j]);
        allsum = 0;
        rep(i, h)rep(j, w) allsum += U[i][j];
        sum2D(h, w);
        rep(i, 35)rep(j, 35)rep(k, 35)rep(l, 35) dp[i][j][k][l] = make_pair(0, INF);
        auto ans = dfs(0, 0, h - 1, w - 1);
        printf("%d %d\n", ans.fi, ans.se);
    }
    return 0;
}