그리디 알고리즘의 대표주자문제이다.
문제
반례를 찾아보면 쉽게 안된다는걸 알 수 있다.
3
1 10
2 3
4 5
이런 예제가 있다면 실제로 가능한 값은 (2, 3) (4, 5) 이겠지만, 우리가 받아볼 결과는 1, 10하나이다.
따라서 이렇게는 안된다.
3
1 10
2 3
4 5
이런 예제가 오더라도
2 3
4 5
1 10
이렇게 정렬하면 문제를 풀 수 있다.
현재 회의가 끝나는 시간을 curr로 잡고, for문으로 전체 회의를 조회하면서, 다음회의의 시작시간이 현재회의의 끝나는 시간보다 늦다면 (크다면) cnt++ 한 후, 다음 회의 끝나는 시간을 curr로 잡는것이다.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int n;
struct Point {
int start;
int end;
};
vector<Point> vec;
bool comp (Point a, Point b) {
if (a.end == b.end) return a.start < b.start;
else return a.end < b.end;
}
int main() {
cin >> n;
for (int i = 0; i < n; i++) {
Point p;
cin >> p.start >> p.end;
vec.push_back(p);
}
sort (vec.begin(), vec.end(), comp);
// for (int i = 0; i < n; i++) {
// cout << vec[i].start << " " << vec[i].end << "\n";
// }
int curr = vec[0].end;
int ans = 1;
for (int i = 1; i < n; i++) {
if (curr <= vec[i].start) {
ans++;
curr = vec[i].end;
}
}
cout << ans;
}
쉬운문제인데 좀 많이 틀렸다.
bool comp 과정에서 a.end == b.end로 비교해야하는데, a.start == b. start로 비교해버렸다...
작은수 - 큰수
이 구조가 나오면 음수가 나오는것을 알 수 있다.
따라서 첫번째 마이너스 뒤를 먼저 다 계산하여 가장 큰 수를 만들어놓고, 맨 앞에 수에서 이를 빼는것이 정답인것이다.
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
using namespace std;
bool flag = false;
string str;
int temp = 0;
int sum = 0;
int main( ){
cin >> str;
for (int i = 0; i <= str.size(); i++) {
if (str[i] == '+'|| str[i] == '-' || i == str.size()) {
if (!flag) {
sum += temp;
temp = 0;
} else if (flag) {
sum -= temp;
temp = 0;
}
if (str[i] == '-') flag = true;
} else {
temp *= 10;
temp += str[i] - '0'; }
}
cout << sum;
}
이 아이디어는 가지고 있었는데, 이를 구현하는 과정에서 조금 어려움을 겪었다.
수가 무조건 한자리수만 있는것은 아니다보니, 문자화 된 수를 처리해주려면
temp *= 10;
temp += str[i] - '0';
위와 같은 처리가 필요했다.
파이썬이나 자바와는 다르게 cpp에서는 char숫자를 int로 바꾸려면
char a = i;
int n = a - '0';
이라는 특수한 공식을 사용해야하기에 이를 기억해두면 좋을것같다.
또 자주 나오는 문제이다. 이를 응용하여 번갈아가면서 들리거나 교차하는 문제도 종종 나온다.
현재 주유소의 가격이 다음 주유소보다 싸다면, 다음에 이동할 거리분까지 지금 다 넣고 이동하자는 전략이다.
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
int n, val;
int ans = 0;
int distance_sum = 0;
vector <int> distancer;
vector <int> price;
int main() {
cin >> n;
for (int i = 0; i < n-1; i++) {
cin >> val;
distance_sum += val;
distancer.push_back(val);
}
for (int i = 0; i < n; i++) {
cin >> val;
price.push_back(val);
}
price.back() = 1000000001;
int min_num = 1000000001;
for (int i = 0; i < n; i++) {
min_num = min(price[i], min_num);
}
int dis_counter = 0;
for (int i = 0; i < n; i++) {
if (price[i] == min_num) {
int temp = price[i] * distance_sum;
ans += temp;
break;
} else {
int temp = price[i] * distancer[dis_counter];
ans += temp;
distance_sum -= distancer[dis_counter];
}
dis_counter++;
}
cout << ans;
}
값이 가장 적은 주유소에서 나머지 기름을 다 넣고, 그 전까지는 자기 다음칸까지 갈 만큼만 주유하는 방식이다.
이 방식의 문제점은, 항상 최적의 방식을 보장하지 못한다는점이다.
#include <iostream>
#include <algorithm>
using namespace std;
long long dist[100000];
long long price[100000];
long long n, curr, ans = 0;
int main() {
cin >> n;
for (int i = 1; i < n; i++) {
cin >> dist[i];
}
for (int i = 0; i < n; i++) {
cin >> price[i];
}
curr = price[0];
ans = price[0] * dist[1];
for (int i = 1; i < n; i++) {
if (curr < price[i]) {
ans += curr * dist[i+1];
} else {
curr = price[i];
ans += curr * dist[i+1];
}
}
cout << ans;
}
위에서 말한것처럼 다음 주유소보다 싸면 계속 주유해나가는 시스템이다.
이렇게 풀면 항상 최적의 결과를 보장한다 할 수 있다.
다만, 문제 요구조건 단위가 꽤 크니깐 long long형을 사용해야 100점을 받을 수 있다.