Table of Contents
문제
지정된 구간에서의 제한 속도와 시험 운행 기록이 주어진다. 운행 구간 중 제한 속도를 가장 많이 초과했을 때 제한 속도와 운행 속도의 차이를 구하라.
나는 구현 문제가 그렇게 귀찮더라
계획
일단 사람 머리로 한번 풀어보자. 문제 예제 그대로 하면 첫 구간은 50m지만 시험 운행을 할 때는 한번에 60m를 달렸으니 첫 번째 구간과 두 번째 구간이 섞여있다. 여기서 첫 번째 구간은 26 초과, 두 번째 구간은 36 초과했다. 이후는 이 이상 초과한 구간이 없으므로 답은 36을 출력해야 맞다.
구간을 비교할 때는 주어진 구간과 시험 운행한 구간 중 작은 것을 기준으로 큰 것을 잘랐다, 라기 보다는 지정 구간은 굳이 자를 필요가 없고 운행 구간만 자르면 될 것 같지 않나? 덧뺄셈으로 뭔가 해결이 될 것 같은데.
일단 생각나는 방법은 두 가지.
- 시뮬레이션 방식으로 1m씩 전진하면서 매번 속도 비교하고 최대 위반 속도 구하기: 불필요한 계산이 너무 많기 때문에 쓰면 안 되는 방법이다. 라고 아래 방법 틀리고 풀이 찾아보기 전까지 그렇게 생각했었다..
- 시험 운행 구간 기준으로 구간마다 나아가되, 지정 구간과 비교하여 겹치는 구간에 대해 속도 위반 계산하기: 이 요약된 서술 안에서 세부 과정을 얼마나 간단하게 쓰느냐에 따라 다르겠지만 일단은 괜찮아 보임.
무엇을 저장해야 하나?
- 지정 구간과 속도: 먼저 입력되기 때문에 전부 저장하고 있어야 함.
- 운행 구간과 속도: 나중에 입력되기 때문에 입력받는 것과 동시에 계산한다면 전부 저장할 필요는 없음. 근데 그거 뭐 얼마나 한다고 int 4개 줄여서 무슨 용량을 줄이겠다고 그러나 싶긴 한데 이걸 1000m로 확장한다 더 크게 한다 생각하면 유효할 수는 있을듯
- 최고 위반 속도 차: 이게 답이니까 알아야지
- 현재 운행 중인 구간이 몇 번째인지 / 직전까지 운행한 구간이 어디였는지: 이 부분이 아직 좀 불확실함. 이걸 어덯게 다뤄야 내가 생각한 흐름대로 가나?
알고리즘 써보기
- 지정 구간과 속도를 입력받는다 → 구간은 누적합으로 저장한다.
- 운행 구간을 입력 받으면서 다음을 수행한다.
- 누적 주행 구간을 계산하고, 이번 구간에서 지나간 구간을 찾는다. → 이번 주행 구간의 누적 시작점과 누적 도착점을 기록해서, 그 사이에 있는 지정 구간만 찾아낸다.
- 해당 지정 구간 내에서 지정 속도와 운행 속도의 차를 계산하고 최댓값 갱신.
- 운행을 완료할 때까지 위 과정 반복
다시 계획
- reference: [Softeer][level2] GBC - 파이썬(Python)
- 지정 구간과 속도를 입력받으면서, 1m 단위로 제한 속도를 배열에 저장한다.
- 운행 구간을 누적합으로 입력받으면서 다음을 수행한다.
- 현재 구간 내에서 1m 단위로 제한 속도 위반 차이 최댓값을 갱신한다.
- 운행을 완료할 때까지 반복
아니.. 그래도 이것보다 더 적은 계산으로 풀 수 있는 방법이 있는 게 아냐? 찾아보니까 다르게 쓴 사람이 있긴 했는데, 풀이도 아무 설명도 없이 딱 코드만 올려놔서 좀 알아먹기는 어려웠다. 다른 문제 더 풀다가 나중에 다시 보게 되면 딱 이해가 되는 순간이 있겠지. → 이렇게 써놓고 직접 고쳤음
풀이
이게 10m 주행하고 20m 더 주행했다 하면 0m ~ 10m까지, 11m ~ 20m까지 구간을 나눠야 하는데, 풀이를 고치니까 저 1 차이가 걸리더라. 생각하지 못한 부분이었음. 그리고 틀린 풀이도 반례 찾아보니까 그 1 차이가 문제였던 것 같다. 저런.
하지만 내가 또 이런 일 생기면 승부욕 돈다고 시간낭비 좀 하는 사람이지. 시간을 더 들여서 원래 내 풀이도 다 고쳤다. 결국 이분 탐색은 뺐지만.
오답
특정 값 이상이면서 그 다음 값 미만인 인덱스를 찾는 이분 탐색을 구현해보려고 했는데, 채점이 틀린 걸 봐선 뭔가 틀린 구석이 있는 거겠지?
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
#include <iostream>
#include <vector>
using namespace std;
int find_section(vector<pair<int, int>>& rule, int start, int before = 0) {
int left = before, right = rule.size() - 1, mid = right / 2;
while (left < right) {
if (start >= rule[mid].first) {
left = mid;
mid = (left + right) / 2;
if (right - left <= 1)
return left;
}
if (start < rule[mid].first) {
right = mid;
mid = (left + right) / 2;
if (right - left <= 1)
return right;
}
}
return 0;
}
int main()
{
// 빠른 입출력
ios::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);
int n, m, temp;
int idx = 0;
vector<pair<int, int>> rule; // 지정 구간
pair<int, int> current; // 현재 주행 누적 구간
int max_speeding = 0; // 속도위반 최댓값
cin >> n >> m;
cin >> current.first >> current.second;
rule.emplace_back(current.first, current.second); // 구간, 속도
for (int i = 1; i < n; i++) {
cin >> current.first >> current.second;
rule.emplace_back(rule[i - 1].first + current.first, current.second);
}
current.first = 0;
for (int i = 0; i < m; i++) {
cin >> temp >> current.second;
temp += current.first;
idx = find_section(rule, current.first + 1, idx);
while (idx < rule.size()) {
max_speeding = (current.second - rule[idx].second > max_speeding ? current.second - rule[idx].second : max_speeding);
if (temp <= rule[idx].first)
break;
idx++;
}
current.first = temp;
}
cout << max_speeding << "\n";
return 0;
}
정답 - 전구간 탐색
이 방법은 마지막 방법이라고 생각했는데 마지막 방법이 아니게 될 줄이야.
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
#include <iostream>
#include <vector>
using namespace std;
int main()
{
// 빠른 입출력
ios::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);
int n, m, temp = 0;
vector<int> rule = vector<int>(101); // 지정 구간
pair<int, int> current; // 현재 주행 누적 구간
int max_speeding = 0; // 속도위반 최댓값
cin >> n >> m;
current.first = 0;
for (int i = 0; i < n; i++) {
cin >> temp >> current.second;
temp += current.first;
for (int j = current.first + 1; j <= temp; j++)
rule[j] = current.second;
current.first = temp;
}
current.first = 0;
for (int i = 0; i < m; i++) {
cin >> temp >> current.second;
temp += current.first;
for (int j = current.first + 1; j <= temp; j++) {
if (current.second - rule[j] > max_speeding)
max_speeding = current.second - rule[j];
}
current.first = temp;
}
cout << max_speeding << "\n";
return 0;
}
정답 - 동시 진행 탐색
마음에 들어. 나는 이렇게 계산하고 싶었단 말야. 필요한 만큼만 저장하고 필요한 만큼만 비교하기.
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
#include <iostream>
#include <vector>
using namespace std;
int main()
{
// 빠른 입출력
ios::sync_with_stdio(false); cin.tie(NULL); cout.tie(NULL);
int n, m, temp;
int idx = 0;
vector<pair<int, int>> rule; // 지정 구간
pair<int, int> current; // 현재 주행 누적 구간
int max_speeding = 0; // 속도위반 최댓값
cin >> n >> m;
cin >> current.first >> current.second;
rule.emplace_back(current.first, current.second); // 구간, 속도
for (int i = 1; i < n; i++) {
cin >> current.first >> current.second;
rule.emplace_back(rule[i - 1].first + current.first, current.second);
}
current.first = 0;
for (int i = 0; i < m; i++) {
cin >> temp >> current.second;
temp += current.first;
if (current.first >= rule[idx].first)
idx++;
while (idx < rule.size()) {
max_speeding = (current.second - rule[idx].second > max_speeding ? current.second - rule[idx].second : max_speeding);
if (temp <= rule[idx].first)
break;
idx++;
}
current.first = temp;
}
cout << max_speeding << "\n";
return 0;
}