문제링크: www.acmicpc.net/problem/2629
반례를 못찾아서 고생한 문제입니다.
양팔저울을 통해서 구슬의 무게를 잴 수있는지 확인하는 문제입니다. 모든 경우를 확인해주어야 합니다.
근데 이때 재귀를 사용하여 해결할 수 있고 dp를 활용하자는 생각을 가지는 것이 문제 접근의 핵심입니다.
첫 번째, dp구현하기
dp[i][j]는 i는 현재 무게, j는 탐색하는 추의 인덱스 입니다.
두 번째, 재귀 점화식 도출
모든 경우를 다 고려해야 합니다. 즉,
ret = solved(abs(weight - sinker[i]), i + 1);
ret = max(ret, solved(weight, i + 1));
ret = max(ret, solved(weight + sinker[i], i + 1));
3가지를 고려해야 합니다.
이때 abs(weight - sinker[i]) 부분 때문에 고생했습니다.
저는 처음에
if (sinker[i] <= weight) {
ret = solved(weight - sinker[i], i + 1);
}
이렇게 구현했습니다. 근데 이때 추보다 작은 무게가 먼저 들어올때 추를 사용하지 않고 넘어가다보니 오류가 났습니다.
해결하기위해서 2가지 방법이 있습니다.
1. 인덱스를 사용하기
2. 그냥 절대값으로 해결하기
저는 2번을 선택했습니다. 절대값으로 고려하여도 저울의 어느쪽에 두는지의 차이이기 때문에 상관이 없습니다.
아래는 정답코드입니다.
코드를 천천히 따라가면서 이해해보세요
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
int a = 0, b = 0;
int sinker[31] = { 0, };
int arr[7] = { 0, };
int dp[100001][31];
int solved(int weight, int current) {
if (weight == 0)
return 1;
if (current > a)
return 0;
int &ret = dp[weight][current];
if (ret != -1)
return ret;
ret = 0;
for (int i = current; i <= a; i++) {
ret = solved(abs(weight - sinker[i]), i + 1);
ret = max(ret, solved(weight, i + 1));
ret = max(ret, solved(weight + sinker[i], i + 1));
return ret;
}
}
int main() {
ios_base::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> a;
for (int i = 1; i <= a; i++)
cin >> sinker[i];
cin >> b;
for (int i = 1; i <= b; i++) {
int temp;
cin >> temp;
memset(dp, -1, sizeof(dp));
if (solved(temp, 1))
cout << 'Y';
else
cout << 'N';
if (i != b)
cout << ' ';
}
cout << endl;
return 0;
}
'알고리즘 > DP' 카테고리의 다른 글
[백준] 1937-욕심쟁이 판다(C++) (0) | 2020.12.29 |
---|---|
[백준] 1520-내리막 길(C++) (0) | 2020.12.28 |
[백준] 4811-알약(C++) (0) | 2020.12.24 |
[백준] 2533-사회망 서비스(SNS)(C++) (0) | 2020.12.23 |
[백준] 18427-함께 블록 쌓기(C++), 배낭알고리즘 (0) | 2020.12.23 |