Lilliput Steps

小さな一歩から着実に. 数学やプログラミングのことを書きます.

AOJ 1176 - Planning Rolling Blackouts

問題文 : 輪番停電計画

解法 :

$dp[sy][sx][gy][gx] := sy \leqq y \leqq gy,\ sx \leqq x \leqq gx$ を満たす区間でのグループの数の最大値と, 供給力の最大値として, メモ化再帰を行うと楽.
列 or 行を降りていき, 分割が不可能な域に達したらimpossible を表すような状態を返してやれば良い.


コード :

#include <cstdio>
#include <cstring>
#include <vector>

using namespace std;

int h, w, s;
int p[32][32], sum[32][32];
pair<int, int> dp[32][32][32][32];

int getSum(int sy, int sx, int gy, int gx)
{
	int ret = sum[gy][gx];
	if (sy) ret -= sum[sy - 1][gx];
	if (sx) ret -= sum[gy][sx - 1];
	if (sy && sx) ret += sum[sy - 1][sx - 1];
	
	return (ret);
}

pair<int, int> calc(int sy, int sx, int gy, int gx)
{
	if (~dp[sy][sx][gy][gx].first) return (dp[sy][sx][gy][gx]);
	
	int cost = sum[h - 1][w - 1] - getSum(sy, sx, gy, gx);
	if (cost > s) return make_pair(0, -1);
	pair<int, int> res = make_pair(1, -cost);
	pair<int, int> a, b, c;
	
	for (int i = sy; i <= gy - 1; i++){
		a = calc(sy, sx, i, gx); b = calc(i + 1, sx, gy, gx);
		if (a.first == 0 || b.first == 0) continue;
		c = make_pair(a.first + b.first, min(a.second, b.second));
		if (res < c) res = c;
	}
	
	for (int i = sx; i <= gx - 1; i++){
		a = calc(sy, sx, gy, i); b = calc(sy, i + 1, gy, gx);
		if (a.first == 0 || b.first == 0) continue;
		c = make_pair(a.first + b.first, min(a.second, b.second));
		if (res < c) res = c;
	}
	
	return (dp[sy][sx][gy][gx] = res);
}

int main()
{
	while (scanf("%d %d %d", &h, &w, &s) && w){
		for (int i = 0; i < h; i++)
			for (int j = 0; j < w; j++)
				scanf("%d", &p[i][j]);
		
		for (int i = 0; i < 32; i++)
			for (int j = 0; j < 32; j++)
				for (int k = 0; k < 32; k++)
					for (int l = 0; l < 32; l++)
						dp[i][j][k][l].first = dp[i][j][k][l].second = -1;
		
		memset(sum, 0, sizeof(sum));
		
		for (int i = 0; i < h; i++){
			for (int j = 0; j < w; j++){
				sum[i][j] += p[i][j];
				if (i) sum[i][j] += sum[i - 1][j];
				if (j) sum[i][j] += sum[i][j - 1];
				if (i && j) sum[i][j] -= sum[i - 1][j - 1];
			}
		}
		
		pair<int, int> res = calc(0, 0, h - 1, w - 1);
		printf("%d %d\n", res.first, s + res.second);
	}
	
	return (0);
}