2 분 소요

저울 (백준 Gold 2)

https://www.acmicpc.net/problem/2437

주어지는 N개의 추들을 이용하여
무게를 ‘젤 수 없는’ 가장 작은 양수의 크기를 구하기

첫 제출 코드

#include<iostream>
#include<vector>
#include<unordered_set>
#include<algorithm>

using namespace std;

bool recur(const vector<int>& stat, unordered_set<int>& vSet, int target, int count, int now, int nowStart)
{
	int n = stat.size();
	if (count > n || now > target)
	{
		return false;
	}

	vSet.insert(now);

	if (now == target)
	{
		return true;
	}

	for (int i = nowStart + 1; i < n; i++)
	{
		if (recur(stat, vSet, target, count + 1, now + stat[i], i))
			return true;
	}

	return false;
}

int main()
{
	int n;
	cin >> n;
	vector<int> stat(n);
	unordered_set<int> valueSet;
	for (int i = 0; i < n; i++)
	{
		cin >> stat[i];
		valueSet.insert(stat[i]);
	}

	sort(stat.begin(), stat.end());

	int value = 0;

	while (true)
	{
		value++;
		if (valueSet.find(value) != valueSet.end())
			continue;

		// value를 현재 가진 수들로 만들 수 있는가?
		if (recur(stat, valueSet, value, 0, 0, -1))
			continue;

		break;
	}

	cout << value;

	return 0;
}

시간초과 발생

문제 자체에 주어진 시간이 너무 적었기에
이 방식으로 시간초과가 발생하였다

현재 코드처럼 ‘일일이’ 세는 방법은
정답이 아니며, 뭔가 놓치고 있다고 생각하였다

풀이방법

고려할 점을 다시 생각해 보았다

  • 추는 결국 ‘더해서’ 무게를 잰다
  • 결국 구하는 것은 ‘추로 구할 수 없는 무게’지만
    그 중 ‘가장 작은 값’

따라서 첫번째 추부터 생각해보자

첫 추 : w1
- 만약 1보다 크다면
  1이 정답
- 아니라면 일단 다음 후보로는 w1 + 1 을 생각

당연히 가장 작은 자연수는 1이므로
첫 추가 1보다 크다면 1이 정답이다

그렇지 않다면 현재 추를 포함한 다음 값을 고려해야 한다

두번째 추 : w2
- 만약 w2가 w1 + 1 보다 크다면
  w1 + 1 이 정답
- 아니라면 다음 후보로 (w1 + w2) + 1 을 생각

따라서 여태까지의 무게 R(현재 w1 + w2)과 다음 추를 비교하는 방식으로
답을 구할 수 있다

고민할 점
사실 제일 헷갈린 부분인데
‘중간 값’은 고려하지 않아도 되는가?

경우의 수를 봐보자

  • 만약 다음 무게 추(Wk)가 R+1 보다 ‘크다면’
    R 이 ‘이전 추’까지의 무게를 사용해 표현 가능한 최대 무게
    따라서 R + 1이 ‘표현 불가능’한 가장 작은 값

  • 만약 다음 무게 추(Wk)가 R+1 보다 ‘작다면’
    R + Wk 까지 ‘표현 가능’한 수가 됨

우리는 ‘오름차순’ 정렬을 사전에 해둠
(사실 처음 값이 R : 0 이기에
w1이 1보다 크다면(R+1) R+1이 정답)

1 1 2 3 …
같은 경우는
R이 1 , 2 , 4 , 7 같은 방식으로 늘어나게 됨
(만들 수 있는 수는 {1,2,3,4,5,6,7})

그러나 만약
1 4 10
이런 경우라면 R이 1에서 끝나버림
그러므로 R + 1 인 2가 정답
(해당 방식은 {1,4,5,10,11,14,15}는 만들 수 있음)

수학적 귀납법을 통하여
표현 가능한 수를 점점 넓혀나가는 방식이다

최종 제출 코드

#include<iostream>
#include<vector>
#include<algorithm>

using namespace std;

int main()
{
	int n;
	cin >> n;
	vector<int> stat(n);
	for (int i = 0; i < n; i++)
	{
		cin >> stat[i];
	}

	sort(stat.begin(), stat.end());

	// 가장 작은추가 1이 아니면 그냥 1이 정답이다
	if (stat[0] > 1)
	{
		cout << 1;
		return 0;
	}

	int value = stat[0];

	for (int i = 1; i < n; i++)
	{
		if (stat[i] > value + 1)
		{
			break;
		}

		value += stat[i];
	}

	cout << value + 1;

	return 0;
}

결과

Image

이번 문제를 통해 그리디 문제는 사실
수학과 논리적인 사고가 기반되어야 풀 수 있다는 사실을
다시 깊게 느꼈다

댓글남기기