포스트

BOJ 25406 식사 계획 세우기

sorohue가 PS하는 블로그

BOJ 25406 식사 계획 세우기

문제 링크입니다.

불가능한 경우

계획 상에서 인접한 두 식당의 메뉴는 서로 달라야 합니다. 한 메뉴가 무지하게 많다면 그걸 최대한 욱여넣는 방법을 생각해 봐야 합니다.

예를 들어서 1번 메뉴를 파는 식당이 엄청 많다고 해봅시다. 그러면 식사 계획 상에서 메뉴의 배치는 1 x 1 x 1 … 과 같이 1과 다른 값이 번갈아 나타나는 형태여야 합니다. 이때 쓸 수 있는 1의 개수의 최댓값은 전체 식사 계획의 길이의 절반을 올림한 값입니다.

I AM GREEDY

식사 계획을 앞쪽부터 채워나갈 건데, 직전과 같은 메뉴가 오면 안 된다는 제약을 빼면 그냥 비어있는 상황과 같습니다. 즉, 현재 남은 식사 계획의 길이의 절반 이상인 메뉴가 있으면 그 메뉴를 써 줘야 합니다. 그게 아니라면 그냥 사전 순으로 가장 빠른 식장을 가져옵니다. 이때만 직전 식당과 같은지 확인해 주면 됩니다.

식사 계획을 세우는 게 가능하다면 각 메뉴는 아무리 많아봤자 식사 계획 길이의 절반을 올림한 개수 이하입니다. 이는 남은 식당의 절반 이상이기 때문에, 남은 식당이 홀 수 개면 존재한다면 유일하고, 짝수 개면 최대 2개까지 존재할 수 있습니다.

두 경우는 각각 그때그때 번갈아가면서 절반 이상인 메뉴와 다른 메뉴를 계획에 추가하는 것으로 해결되므로, 이상의 전략이 유효합니다.

정렬성을 유지하는 우선순위 큐 등의 자료 구조를 이용하면 그때그때 가장 수가 많은 메뉴와 가장 사전 순으로 빠른 번호의 식당을 찾아낼 수 있습니다.

코드

대회 때 짠 코드라 좀 더럽네요.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#include <bits/stdc++.h>
using namespace std;
vector< queue<int> > v;
int plan[321098];
int num[321098];
int a[321098];
int cntfromsize[321098];
int cntfromnumber[321098];
int n;

struct rest{
	int idx, minnum, size;
	friend bool operator<(const rest& a, const rest& b){
		if(a.size == b.size){
		  return a.minnum > b.minnum;
		}
		return a.size < b.size;
	}
};

struct Rest{
	int idx, minnum, size;
	friend bool operator<(const Rest& a, const Rest& b){
		if(a.minnum == b.minnum){
			return a.size < b.size;
		}
		return a.minnum > b.minnum;
	}
};

priority_queue<rest> pq_by_size;
priority_queue<Rest> pq_by_number;

int main() {
	cin.tie(0); cout.tie(0); ios::sync_with_stdio(false);
	cin >> n;
	v.resize(n+1);
	int m = -1;
	for(int i = 1; i <= n; i++){
		cin >> a[i];
		v[a[i]].push(i);
		num[a[i]]++;
		if(num[a[i]] > m) m = num[a[i]];
	}
	if(n%2){
		if(m > n/2+1) return !(cout << -1);
	}
	else{
		if(m > n/2) return !(cout << -1);
	}
	for(int i = 1; i <= n; i++){
		if(num[i]){
			rest r;
			Rest R;
			r.idx = R.idx = i;
			r.minnum = R.minnum = v[i].front();
			r.size = R.size = num[i];
			pq_by_size.push(r);
			pq_by_number.push(R);
		}
	}

	int lastidx = 0;
	rest remain;
	Rest Remain;
	bool re = false;
	bool Re = false;
	for(int i = n; i > 0; i--){
		int t = pq_by_size.top().size;
		if((t >= i/2+i%2 && i%2 == 1) || (t > i/2 && t%2 == 0)){
			rest p = pq_by_size.top();
			pq_by_size.pop();
			if(cntfromnumber[p.idx]){
				p.minnum = v[p.idx].front();
				p.size -= cntfromnumber[p.idx];
				cntfromnumber[p.idx] = 0;
				if(p.size > 0) pq_by_size.push(p);
				i++;
				continue;
			}
			else if(lastidx == p.idx){
				re = true;
				remain = p;
				i++;
				continue;
			}
			else{
				cout << p.minnum << ' ';
				v[p.idx].pop();
				p.minnum = v[p.idx].front();
				p.size--;
				lastidx = p.idx;
				if(p.size > 0) pq_by_size.push(p);
				cntfromsize[p.idx]++;
			}
		}

		else{
		   Rest p = pq_by_number.top();
			pq_by_number.pop();
			if(cntfromsize[p.idx]){
				p.minnum = v[p.idx].front();
				p.size -= cntfromsize[p.idx];
				cntfromsize[p.idx] = 0;
				if(p.size > 0) pq_by_number.push(p);
				i++;
				continue;
			}
			else if(lastidx == p.idx){
				Re = true;
				Remain = p;
				i++;
				continue;
			}
			else{
				cout << p.minnum << ' ';
				v[p.idx].pop();
				p.minnum = v[p.idx].front();
				p.size--;
				lastidx = p.idx;
				if(p.size > 0) pq_by_number.push(p);
				cntfromnumber[p.idx]++;
			}
		}
		if(re) pq_by_size.push(remain);
		if(Re) pq_by_number.push(Remain);
		re = Re = false;
	}
}
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.