문제
https://www.acmicpc.net/problem/1060
풀이
문제가 상당히 수학적이라 바로 이해하기 힘들다.
풀이를 해보기 전에 이 글에서 사용할 용어 몇 가지를 약속하고 가자.
- 숫자 집합 S = {s(0), s(1), ... , s(L-1)} 와 같은 형태이다.
- S 구간이란, S의 원소가 아닌 연속된 정수들의 구간이다.
- 예를 들면 S = {s(0), s(1), s(2)} 일 때 S구간은 (1, s(0)), (s(0), s(1)), (s(1), s(2)), (s(2), 무한대) 가 있다.
- S구간의 오른쪽에 오는 원소가 s(n)이면 해당 S구간은 S(n)이라고도 한다.
- x가 포함된 좋은 구간의 갯수는 i다.
어떤 수가 더 좋은 수인지 판단하기 위해서는 어떤 수가 몇 개의 좋은 구간을 가지고 있는지 알아야 한다.
어떤 수 x의 좋은 구간의 갯수 i를 구하기 위해서는 x가 어떤 S구간에 포함된 수인지 알아야한다.
x가 S(n)에 포함되어 있고, x가 포함된 좋은 구간을 [a, b]라고 했을 때, 아래와 같은 조건이 성립된다.
s(n-1) < a <= x <= b < s(n)
a와 b의 가능한 숫자의 범위에 따라 가능한 숫자의 갯수를 구할 수 있다.
a의 가짓수 = (x-s(n-1) +1)
b의 가짓수 = (s(n)-x +1)
그리고 좋은 구간[a, b]의 가능한 가짓수, 즉 좋은 구간의 갯수 i는 a의 가짓수 x b의 가짓수 -1와 같다.
마지막에 1을 빼준 이유는 [x, x]는 불가능하기 때문이다.
따라서 S(n)에 포함된 x의 i의 공식은 아래와 같다.
i = (x-s(n-1) +1) * (s(n)-x +1) -1
그리고 이것을 그래프로 그리면 대략 아래와 같다.
특징적으로 봐야할 부분은 다음과 같다.
- x가 포함된 S구간의 길이가 짧을 수록 i가 작다.
- x가 자신이 포함된 S구간의 중앙에서 멀수록 i가 작다.
- x가 s(n)과 같을 경우 i는 0이 된다.
- 마지막 S구간에 포함된 x들의 i는 무한대다.
즉, 어떤 수가 좋은 수인지는 해당 수가 얼마나 짧은 S 구간에 포함되어 있느냐, 그리고 x가 얼마나 S구간의 가생이에 존재하느냐 가 중요하다.
이 사실을 깨달았다면 본격적인 풀이가 가능하다.
- map에 모든 s로부터의 거리가 n 이하인 수 x와 x에 대한 i를 pair로 만들어 넣는다.
- 그리고 x를 priority_queue에 삽입한다.
- 이 때 priority_queue의 조건자가 중요하다.
- 조건자는 i가 작을수록, i가 같다면 x가 작을수록 높은 우선순위를 갖도록 한다.
- n개의 숫자를 priority_queue에서 pop하고 출력한다.
이 떄 x를 s로부터의 거리가 n 이하인 수로 할 수 있었던 것은, n과 L이 각각 100이하, 50이하이기 때문에 가능한 풀이다. 이 숫자가 커지면 다른 방법을 찾아보아야 할 것이다.
코드
// CodeTest.cpp : 이 파일에는 'main' 함수가 포함됩니다. 거기서 프로그램 실행이 시작되고 종료됩니다.
//
#include <iostream>
#include <vector>
#include <map>
#include <queue>
#include <set>
#include <algorithm>
#include<cstring>
#include <queue>
using namespace std;
int L;
vector<int> S;
int n;
map<int, long long> m;
long long GetSectorCount(int num, int part)
{
long long left = 1;
long long right = 0;
if (part < L && S[part] == num)
return 0;
if (part == 0)
right = S[0]-1;
else if(part == L)
return -1;
else
{
left = S[part-1] + 1;
right = S[part] -1;
}
return (num-left +1) * (right - num +1) -1;
}
struct Cmp
{
bool operator()(int a, int b)
{
long long aCount = m.find(a)->second;
long long bCount = m.find(b)->second;
if (aCount == bCount)
return a > b;
if (aCount == -1)
return true;
if (bCount == -1)
return false;
return aCount > bCount;
}
};
int main()
{
cin >> L;
S.resize(L);
for (size_t i = 0; i < L; i++)
{
int input; cin >> input;
S[i] = input;
}
cin >> n;
sort(S.begin(), S.end());
priority_queue<int, vector<int>, Cmp> q;
for (size_t i = 0; i < L; i++)
{
int left;
int right;
if (i == 0)
{
left = 1;
right = S[0];
}
else
{
left = S[i-1];
right = S[i];
}
int count=0;
for (size_t j = left; j <= right && count < n; j++,count++)
{
if (m.find(j) == m.end())
{
m.insert({ j,GetSectorCount(j,i) });
q.push(j);
}
}
count = 0;
for (int j = right ; j >= left && count < n; j--, count++)
{
if (m.find(j) == m.end())
{
m.insert({ j ,GetSectorCount(j,i) });
q.push(j);
}
}
}
for (size_t i = 1; i < n+1; i++)
{
int val = S[L - 1] + i;
m.insert({val,GetSectorCount(val,L)});
q.push(val);
}
for (size_t i = 0; i < n; i++)
{
cout << q.top()<<' ';
q.pop();
}
}
'수학, 알고리즘' 카테고리의 다른 글
[프로그래머스 Lv3]산 모양 타일링 (0) | 2024.07.09 |
---|---|
[프로그래머스 lv2]도넛과 막대 그래프 (0) | 2024.07.08 |
[백준]1285 동전 뒤집기 (1) | 2024.04.26 |
[백준]1525 퍼즐 (0) | 2024.04.23 |
[백준]1167 트리의 지름 (0) | 2024.04.22 |