백준 1744 - 수 묶기

2 minute read

문제링크

문제설명

  • 길이가 N인 수열이 주어질 때, 그 수열의 합을 구하려고 한다.
  • 그냥 그 수열의 합을 모두 더해서 구하는 것이 아니라, 수열의 두 수를 묶을 수 있다.
  • 두 수는 위치에 상관없이 묶을 수 있지만, 자기 자신과 묶는 것은 불가능하고 딱 한번만 묶을 수 있다.
  • 어떤 수를 묶게 되면 수열의 합을 구할 때 묶은 수는 서로 곱한 후에 더한다.
  • 예를 들면, 어떤 수열이 {0, 1, 2, 4, 3, 5}일 때, 그냥 이 수열의 합을 구하면 0+1+2+4+3+5 = 15이다. 하지만, 2와 3을 묶고, 4와 5를 묶게 되면, 0+1+(2x3)+(4x5) = 27이 되어 최대가 된다.
  • 수열이 주어졌을 때, 수열의 각 수를 적절히 묶어 그 합이 최대가 되게 하라.

문제접근

  • 양수의 경우 가장 큰 두 수끼리 묶는 것이 합을 최대로 만드는 방법이다.
    • 단, 1의 경우 어떤 수와 묶는 것보다 그대로 유지하는 것이 최대 합을 구할 수 있는 방법이므로 1의 개수는 따로 체크하고, 마지막에 따로 더해준다.
  • 음수의 경우도 절댓값이 가장 큰 두 수끼리 묶는 것이 합을 최대로 만드는 방법이다.
    • 음수의 개수가 홀수이면서 0 이 존재한다면, 절댓값이 가장 작은 음수와 0을 묶어 음수를 모두 없앨 수 있다.
    • 양수와는 달리 모든 음수는 묶는 것이 합을 최대로 만드는 방법이다. 따라서, 가능한 모든 음수는 묶어준다.

구현

#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;

int main()
{
	cin.tie(NULL);
	ios_base::sync_with_stdio(false);
	int N;
	cin >> N;
	vector<int> negativeArr, positiveArr;
	bool zero = false;
	int cntOne = 0;
	for (int i = 0; i < N; i++)
	{
		int num;
		cin >> num;
		if (num > 1) positiveArr.push_back(num);
		else if (num < 0)
		{
			num = abs(num);
			negativeArr.push_back(num);
		}
		else if (num == 1) cntOne++;
		else if (num == 0) zero = true; // 0은 음수가 홀수개인 경우 묶이지 않은 절댓값이 가장 작은 음수와 곱해진다.
	}

	// 내림차순 정렬
	sort(positiveArr.begin(), positiveArr.end(), greater<int>());
	sort(negativeArr.begin(), negativeArr.end(), greater<int>());

	vector<int> newArr;
	if (positiveArr.size() > 0)
	{
		// 홀수인 경우 제일 뒤에 있는 수는 먼저 새로운 순열에 넣어두고 짝수로 만든다.
		if (positiveArr.size() % 2 == 1)
		{
			newArr.push_back(positiveArr.back());
			positiveArr.pop_back();
		}
		for (int i = 0; i < (int)positiveArr.size() - 1; i += 2) // 형변환 필수
		{

			newArr.push_back(positiveArr[i] * positiveArr[i + 1]);
		}
	}

	if (negativeArr.size() > 0)
	{
		if (negativeArr.size() % 2 == 1)
		{
			if (zero == false)
			{
				newArr.push_back(negativeArr.back() * (-1));
			}
			// 0이 있는 경우 0과 음수를 묶어서 0으로 만들어줌. 새로운 수열에는 안들어감
			negativeArr.pop_back();
		}
		for (int i = 0; i < (int)negativeArr.size() - 1; i += 2) // // 형변환 필수
		{
			newArr.push_back(negativeArr[i] * negativeArr[i + 1]);
		}
	}
	long long ans = 0;
	for (auto ele : newArr)
	{
		ans += (long long)ele;
	}
	ans += cntOne;
	cout << ans << "\n";
}
  • 벡터 컨테이너를 다룰 때 반복문 조건에 size() 함수를 호출해서 사용해왔는데, size() 함수는 반환 자료형 타입이 size_t 이다.
  • 그동안 size() - 1 처럼 size_t 자료형과 상수 값을 빼는 연산을 할 때 타입에 전혀 신경쓰지 않고 있었는데, size() 값이 0인 경우 해당 반복문 조건이 true 가 되면서 무한 루프에 빠지는 현상을 발견했다.
    • size_tunsigned_int 자료형이기 때문에 -1 값이 할당되면 MSB 가 1로 설정되어 음수가 아니라 unsinged_int 범위에 해당하는 엄청 큰 양수가 된다.(무한루프라고 느꼈던 이유)
    • 암묵적 형변환의 위험성 중 하나… 이번 문제 풀이를 통해 느낀 것이 천만다행이다!
  • 명시적으로 형변환을 해주고 나니 해당 문제가 해결되었다. 명시적 형변환을 습관화하자!

Leave a comment