문제 링크: 링크
오랜만에 알고리즘 공부를 하려니 좀이 쑤시네요;;
SW Expert Academy(SWEA) 9282번을 풀어보겠습니다. 난이도는 D4입니다.
그냥 풀이만 간단하게 올리겠습니다.
일단 이 문제는 2009년 IOI(국제 정보올림피아드) 4번 문제 'Raisins(건포도)' (백준 링크) 와 동일합니다.
초콜릿에 자를 대고 자를 수 있고, 자를 때마다 자르기 전 초콜릿에 있는 건포도만큼을 요금으로 냅니다.
n x m 사이즈 초콜릿을 1 x 1 조각들로 나누는데 드는 최소한의 요금을 구하는 게 문제입니다.
예를 들어 \( \begin{matrix} 2 & 7 & 5 \\ 1 & 9 & 5 \end{matrix} \) 모양을 가진 초콜릿이 있을 때, 위 순서대로 자르면 77의 비용이 나오고 이게 최소 비용입니다.
\[ cost(초콜릿) \\ = min \Biggl( \{ cost(x)+cost(y)+sum(초콜릿) | \text{x와 y는 초콜릿을 자른 두 조각} \} \Biggr) \]
해결방법은 dp(memoization)와 prefix sum입니다. 위 수식을 맞게 표현한건지 잘 모르겠네요;;
위 수식에서 sum은 자르기 전의 초콜릿 위에 있는 건포도를 다 더한겁니다.
자른 초콜릿의 최소 cost는 언제나 동일합니다. 그리고 잘린 초콜릿 조각끼리 서로 영향을 주지 않으니 최적 부분 조건이 성립합니다.
자르는 방법은 재귀적으로 다 잘라보시면 됩니다. 이 때 memoziation을 사용해야 계산 횟수를 줄일 수 있습니다.
자르기 전 건포도 개수를 다 더할 때(sum) 매 번 구하면 시간 초과가 나오니 2d array prefix sum을 사용해야 합니다. 포함배제 원칙 생각해서 만들면 됩니다.
아래는 소스입니다.
#define _CRT_SECURE_NO_WARNINGS
#include <bits/stdc++.h>
using namespace std;
const int INF = 987654321;
const int MAX_LEN = 51;
//y, x, width, height
int cache[MAX_LEN][MAX_LEN][MAX_LEN][MAX_LEN];
int chocolate[MAX_LEN][MAX_LEN];
int subsum[MAX_LEN][MAX_LEN];
int raisin_sum(int y, int x, int width, int height) {
const int& y2 = y+height-1, x2=x+width-1, y1=y, x1=x;
return subsum[y2][x2]
- subsum[y2][x1-1]
- subsum[y1-1][x2]
+ subsum[y1-1][x1-1];
}
int raisin(int y, int x, int width, int height) {
//base
if (width == 1 && height == 1) return 0;
int& ret = cache[y][x][width][height];
if (ret != -1) return ret;
ret = INF;
//가로로 자르기
for (int cut_height = 1; cut_height < height; ++cut_height) {
ret = min(
ret,
raisin(y, x, width, cut_height) + raisin(y+cut_height, x, width, height-cut_height) + raisin_sum(y, x, width, height)
);
}
//세로로 자르기
for (int cut_width = 1; cut_width < width; ++cut_width) {
ret = min(
ret,
raisin(y, x, cut_width, height) + raisin(y, x+cut_width, width-cut_width, height) + raisin_sum(y, x, width, height)
);
}
return ret;
}
int main(int argc, char** argv)
{
freopen("input.txt", "r", stdin);
int test_case;
int T;
int n, m;
cin >> T;
for (test_case = 1; test_case <= T; ++test_case)
{
memset(cache, -1, sizeof(cache));
memset(subsum, 0, sizeof(subsum));
cin >> n >> m;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
cin >> chocolate[i][j];
}
}
//부분합 구하기
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
subsum[i][j] = (j == 0 ? chocolate[i][j] : subsum[i][j - 1] + chocolate[i][j]);
for (int i = 1; i < n; ++i)
for (int j = 0; j < m; ++j)
subsum[i][j] = subsum[i - 1][j] + subsum[i][j];
cout << "#" << test_case << " " << raisin(0, 0, m, n) << "\n";
}
return 0;//정상종료시 반드시 0을 리턴해야합니다.
}
백준이랑 똑같은 문제라서 조금만 수정해서 올리려고 했더니 wrong answer가 나오네요. 왜지?.. ㅠㅠ
+ 2020. 07. 01
드디어 알아냈습니다. 누적합으로 (y,x) ~ (y+height, x+width)에 있는 건포도 계산할 때 x1>0, y1>0을 체크해줘야 합니다. 아니 근데 이러면 메모리 이상한거 찍은거니까 런타임에러를 뿜어야 맞는거 아닌가... 정말 삽질했습니다 휘유
'Online Judge > SW Expert Academy' 카테고리의 다른 글
[SWEA][C++] 5658: [모의 SW 역량테스트] 보물상자 비밀번호 (0) | 2020.04.22 |
---|