보자마자 빡빡한 구현일 것을 직감하였다. 하지만 이런 쉬운 난이도에 빡구현인 문제는 왜 이렇게 하기 싫은걸까..
다른 분들 코드도 구글링해 보았는데 1개 밖에 못 찾았고 그 분은 빡구현으로 하신 듯 했다.
괜히 효율적으로 코딩하고 싶은 마음이 들어서 삽질을 해 보았고 그나마 괜찮은 구현을 했다.
#include <iostream>
#include <algorithm>
#include <cmath>
#include <utility>
#include <string>
#include <cstring>
#include <vector>
#include <tuple>
#include <stack>
#include <queue>
#include <deque>
#include <list>
#include <map>
#include <unordered_map>
#include <climits>
#define INF 987654321
#define INF2 2147483647
#define f first
#define s second
#define all(v) (v).begin(), (v).end()
using namespace std;
using ll = long long;
using pii = pair<int, int>;
using ti3 = tuple<int, int, int>;
bool used[100][100];
int gridX[100], gridY[100];
vector<int> xval, yval;
class rectangle {
public:
int idx;
int x1, y1, x2, y2;
int grid_x1, grid_y1, grid_x2, grid_y2;
int area;
void set_grid() {
grid_x1 = lower_bound(all(xval), x1) - xval.begin();
grid_x2 = lower_bound(all(xval), x2) - xval.begin();
grid_y1 = lower_bound(all(yval), y1) - yval.begin();
grid_y2 = lower_bound(all(yval), y2) - yval.begin();
}
void paintArea() {
this->area = 0;
for(int x=grid_x1; x<grid_x2; x++) {
for(int y=grid_y1; y<grid_y2; y++) {
if (used[x][y]) continue;
used[x][y] = true;
this->area += gridX[x] * gridY[y];
}
}
}
};
bool cmp(rectangle a, rectangle b) {
if(a.area == b.area) return a.idx < b.idx;
return a.area > b.area;
}
int N, K;
vector<rectangle> recs;
int main(void) {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
memset(used, 0, sizeof used);
cin >> N >> K;
recs.resize(N);
for(int i=0; i<N; i++) {
recs[i].idx = i+1;
cin >> recs[i].x1 >> recs[i].y1 >> recs[i].x2 >> recs[i].y2;
xval.emplace_back(recs[i].x1); xval.emplace_back(recs[i].x2);
yval.emplace_back(recs[i].y1); yval.emplace_back(recs[i].y2);
}
sort(all(xval)); sort(all(yval));
xval.erase(unique(all(xval)), xval.end());
yval.erase(unique(all(yval)), yval.end());
for(int i=1; i<xval.size(); i++) {
gridX[i-1] = xval[i] - xval[i-1];
}
for(int i=1; i<yval.size(); i++) {
gridY[i-1] = yval[i] - yval[i-1];
}
for(int i=0; i<N; i++) {
recs[i].set_grid();
}
for(int i=N-1; i>=0; i--) {
recs[i].paintArea();
}
vector<int> ans;
sort(all(recs), cmp);
for(int i=0; i<K; i++) {
ans.emplace_back(recs[i].idx);
}
sort(all(ans));
for(int x : ans) {
cout << x << ' ';
}
cout << '\n';
return 0;
}
수들이 연속적인 부분수열이면서 동시에 LCS인 수열을 찾으면 된다. 이를 연속적인 LCS라 부르겠다.
예를 들면, [1, 2, 3, 5, 6, 4] 인 수열이 주어졌을 때 기존 LCS는 [1, 2, 3, 5, 6]이 될 것이다.
하지만 우리가 찾아야할 것은 LCS이면서 수들이 연속적이어야 하므로 [1, 2, 3, 4]가 연속적인 LCS가 된다.
연속적인 LCS를 찾아야 하는 이유를 알아보자.
주어진 수열은 [1, 2, ... , N]이고, 연속적인 LCS가 [a, a+1, a+2, ..., b] 라고 하자.
그렇다면 주어진 수열에서 a보다 작은 수들은 a-1부터 1의 순서로 맨 앞으로 보내고,
b보다 큰 수는 b+1부터 N의 순서로 맨 뒤로 보낸다.
따라서 정답은 N - [연속적인 LCS의 길이] 가 된다.
#include <iostream>
#include <algorithm>
#include <cmath>
#include <utility>
#include <string>
#include <cstring>
#include <vector>
#include <tuple>
#include <stack>
#include <queue>
#include <deque>
#include <list>
#include <map>
#include <unordered_map>
#include <climits>
#define INF 987654321
#define INF2 2147483647
#define f first
#define s second
#define all(v) (v).begin(), (v).end()
#define all1(v) (v).begin()+1, (v).end()
using namespace std;
using ll = long long;
using pii = pair<int, int>;
using ti3 = tuple<int, int, int>;
int N;
vector<int> idx;
int main(void) {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
cin >> N;
idx.resize(N+1);
for(int i=1; i<=N; i++) {
int val; cin >> val;
idx[val] = i;
}
int longest = 0, val, prob;
for(val = 1, prob = 1; val < N; val++, prob++) {
if(idx[val] > idx[val+1]) {
longest = max(longest, prob);
prob = 0;
}
}
cout << N - max(longest, prob) << '\n';
return 0;
}
1시간 4분 7초
우선순위 큐 다익스트라인데 우선순위 큐를 큐로 바꾸면 풀리는 문제다.
(이전에는 우선순위큐도 통과되었던 듯 하다.)
그 이유를 도저히 모르겠어서 백준 질문게시판을 다 찾아보고 구글링도 해봤는데 아직도 확실히는 모르겠다.
대체적인 이유는 일반적인 최소 경로 탐색 문제와는 다르게 조건이 있어서 worst case의 경우 모든 Edge의 수가 가 되어버린다.
비용을 매번 초과해버리는 경우 노드를 M번 방문하게 되고 방문할 때마다 또 다시 모든 edge를 방문하게 되므로 총 Edge의 수는 가 되어버린다.
따라서 우선순위큐 시간복잡도가 인데 E = MK = 10^8 이므로 시간초과가 되어버리는 듯 하다.
반면에 큐는 거리가 적은 순으로 정렬하지 않기 때문에 우직하게(?) 탐색하고 worst case를 만나지 않는 듯 하다.
조금 더 추측해보자면,
입력으로 주어지는 테스트케이스들 중 몇 개의 데이터셋이 우선순위 큐에서 worst-case를 무조건적으로 발생시키는 dataset이라고 하자.
이것이 큐로 했을 때 우선순위 큐보다 worst-case를 만날 확률이 낮을 수 밖에 없다. 아마도 백준의 채점 데이터셋에서 몇 개의 인풋들에 대하여 우선순위 큐가 큐에 비해 압도적으로 worst한 case들을 갖는 것 같다. 30%대의 인풋들을 의심해본다
#include <iostream>
#include <algorithm>
#include <cmath>
#include <utility>
#include <string>
#include <cstring>
#include <vector>
#include <tuple>
#include <stack>
#include <queue>
#include <deque>
#include <list>
#include <map>
#include <unordered_map>
#include <climits>
#define INF 987654321
#define INF2 2147483647
#define f first
#define s second
#define all(v) (v).begin(), (v).end()
using namespace std;
using ll = long long;
using pii = pair<int, int>;
using ti3 = tuple<int, int, int>;
const int MXN = 101;
const int COST_MXN = 1e4+1;
queue<ti3> q; // init complete
int N, M, K; // init complete
vector<ti3> g[MXN]; // g[here] := (there, 비용, 거리) init complete
int dist[MXN][COST_MXN]; // init complete
// start = 1, end = N
string dijkstra() {
int ans = INF;
dist[1][0] = 0; // dist[위치][비용] := 거리
q.emplace(0, 0, 1); // (거리, 비용, 위치)
while(!q.empty()) {
int hereDist = get<0>(q.front()), hereCost = get<1>(q.front()), here = get<2>(q.front());
q.pop();
if(here == N) ans = min(ans, hereDist);
if(dist[here][hereCost] < hereDist) continue;
for(ti3 edge : g[here]) {
int there = get<0>(edge), thereCost = hereCost + get<1>(edge), thereDist = hereDist + get<2>(edge);
if(thereCost > M) continue;
if(dist[there][thereCost] <= thereDist) continue;
dist[there][thereCost] = thereDist;
q.emplace(thereDist, thereCost, there);
}
}
if(ans == INF) return "Poor KCM";
return to_string(ans);
}
int main(void) {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int T; cin >> T;
while(T--) {
cin >> N >> M >> K;
// init
for(int i=1; i<=N; i++) vector<ti3>().swap(g[i]); // init g
while(!q.empty()) q.pop(); // init q
fill(&dist[0][0], &dist[MXN-1][COST_MXN], INF); // init dist
while(K--) {
int u, v, c, d; cin >> u >> v >> c >> d;
g[u].emplace_back(v,c,d);
}
cout << dijkstra() << '\n';
}
return 0;
}