프로그래머스 디펜스 게임
앞에서부터 priority queue를 써서 제일 큰 값을 줄여가면 되는 문제. 단순 구현이다.
typedef long long ll; int solution(int n, int k, vector<int> enemy) { priority_queue<ll> pq; int i = 0; ll sum = 0; while(1){ if(i < enemy.size()){ if(n >= sum + enemy[i]){ // 이번 wave 감당 가능하면 넣음 sum += enemy[i]; pq.push(enemy[i]); i++; } else if(k > 0){ // 이번 wave 감당 불가면 무적건 써야 함. 없으면 불가 sum += enemy[i]; pq.push(enemy[i]); i++; k--; sum -= pq.top(); pq.pop(); } else break; } else break; } return i; }
시간복잡도
전체 array를 순회하고, pq를 쓰므로 O(nlogn)
프로그래머스 시소 짝꿍
상당히 귀찮은 문제. 처음에는 lower_bound로 구할려 했는데 이러면 내가 원하는 값이 있는지 없는지가 아니라 그거보다 큰 값이 나온다. 그리고 indexing도 귀찮고. 그래서 map을 쓰는 게 좋다.
- map에 무게가 i인 사람 명수를 저장한다.
- weights를 순회하면서 해당 몸무게를 가진 사람과 짝궁이 될 수 있는 후보군을 추린다.
- setAbles 함수를 사용한다. 만약 % 결과가 0이라면 해당 몸무게는 존재한다(정수값이니까) 그렇지 않으면 존재하지 않음.
- 그 후보군의 무게 명수를 answer에 더한다. 단, 후보군 무게 == 현재 탐색중인 사람 몸무게인 경우에는 그 사람을 빼야 하므로 -1을 한다.
- answer / 2를 리턴한다.(중복되는 경우의 수가 있기 때문)
#include <string> #include <vector> #include <algorithm> #include <map> #include <iostream> using namespace std; map<int, int> m; // m[i] : 무게가 i인 사람 명수 // w에 해당하는 배율이 가능한 위치 찾음 typedef pair<int, int> pii; vector<pii> positions; vector<int> ables(7); void setAbles(int w){ for(int i = 0; i<positions.size(); i++){ int left = positions[i].first * w; if(left % positions[i].second){ ables[i] = -1; } else{ ables[i] = left / positions[i].second; } } } long long solution(vector<int> weights) { positions = {{2, 2}, {2, 3}, {2, 4}, {3, 2}, {3, 4}, {4, 2}, {4, 3}}; for(int w : weights){ if(m.find(w) == m.end()) m[w] = 1; else m[w]++; } long long answer = 0; for(int i : weights){ setAbles(i); for(int a : ables){ if(a == -1) continue; if(m.find(a) != m.end()){ answer += m[a] - (a == i); } } } return answer/2; }
시간복잡도
map insert, search는 O(logn). for문은 O(n). O(nlogn)이다.
프로그래머스 미로 탈출
간단한 BFS 문제. 항상 그렇듯 BFS에서 visited는 q에 넣는 순간이다. 그리고 좌표평면에서 탐색할 때는 dr[4], dc[4]를 쓰자. 편하다.
문제는 [시작점 - 레버], [레버 - 도착점]의 최단거리를 찾는 것이다. 좌표평면에서 최단거리 탐색이기 때문에 BFS를 사용하면 된다.
int dr[4] = {1, 0, -1, 0}; int dc[4] = {0, 1, 0, -1}; bool visited[101][101]; vector<string> Maps; int maxr, maxc; struct Search{ int r, c, dist = 0; }; void initVisited(){ for(int r = 0; r<maxr; r++){ for(int c = 0; c<maxc; c++){ visited[r][c] = false; } } } int BFS(Search start, Search end){ queue<Search> q; q.push(start); visited[start.r][start.c] = true; while(!q.empty()){ Search cur = q.front(); q.pop(); int cr = cur.r, cc = cur.c, cd = cur.dist; if(cr == end.r && cc == end.c) return cd; for(int i = 0; i<4; i++){ int nr = cr + dr[i], nc = cc + dc[i]; if(0 <= nr && nr < maxr && 0 <= nc && nc < maxc && !visited[nr][nc] && Maps[nr][nc] != 'X'){ Search next; next.r = nr; next.c = nc; next.dist = cd + 1; visited[nr][nc] = true; q.push(next); } } } return -1; } int solution(vector<string> maps) { Maps = maps; maxr = maps.size(); maxc = maps[0].size(); Search start, lever, exit; for(int r = 0; r < maxr; r++){ for(int c = 0; c<maxc; c++){ if(maps[r][c] == 'S'){ start.r = r; start.c = c; } else if(maps[r][c] == 'E'){ exit.r = r; exit.c = c; } else if(maps[r][c] == 'L'){ lever.r = r; lever.c = c; } } } initVisited(); int start2lever = BFS(start, lever); initVisited(); int lever2exit = BFS(lever, exit); if(start2lever == -1 || lever2exit == -1) return -1; return start2lever + lever2exit; }
시간복잡도
BFS는 O(V + E)인데 E = 4V이므로 O(V)
Leetcode 2444. Count Subarrays With Fixed Bounds
첫 접근은 [범위 밖에 나가는 것들을 다 빼고 생각하자] 였다. 그러나 이렇게 해도 [1, 1, 1, 1]과 같은 경우를 처리하기 힘들었다. 어찌저찌 two-pointer 같다는 생각은 했으나... 잘 모르겠어서 solution을 봤다. 처음에는 보고도 "대체 왜 이게 답임" 이 생각을 했다.
조금 풀어서 보자. 앞에서부터 순회한다고 생각하자.
일단 nums[i]가 maxK보다 크거나, nums[i]가 minK보다 작다면 i는 무조건 subarray에 속하지 않는다. 이건 문제 조건에 의해 이렇게 될 수 밖에 없다. i+1부터 subarray를 만든다고 가정하자. 이 때 index를 start라고 하자.
그러면 나머지 경우는 어떨까? minK <= nums[i] <= maxK인 경우. 이 때 array는 무조건 start+1부터 시작일 것이다.
- minK, maxK가 둘 중 하나라도 발견되지 않은 경우, subarray가 만들어지지 않으므로 더 탐색해야 한다.
- minK, maxK가 발견되어 있는 경우
- 새로 들어온 수(nums[i])가 minK 또는 maxK인 경우 : 이 경우 새로운 subarray를 구성할 수 있다.
- nums[i]가 minK인 경우, 원래 maxK부터 nums[i]까지가 subarray를 구성한다.
- start | start + 1 , ... , [lastMax, ... , i]
- 괄호 친 부분이 꼭 들어가야 하는 subarray이고, 괄호 왼쪽부터 start + 1까지 넣을 수 있다. 연속되어야 하므로 lastMax- start이다.
- nums[i]가 maxK인 경우, 원래 minK부터 nums[i]까지 subarray를 구성한다.
- start | start + 1 , ... , [lastMin, ... i]
- 괄호 친 부분이 꼭 들어가야 하는 subarray이고, 괄호 왼쪽부터 start + 1까지 넣을 수 있다. 연속되어야 하므로 lastMin- start이다.
- nums[i]가 minK인 경우, 원래 maxK부터 nums[i]까지가 subarray를 구성한다.
- 새로 들어온 수가 minK < nums[i] < maxK인 경우
- start | start + 1 , ... , [[minK와 maxK가 있는 subarray], nums[i]]
- 위와 같이 표현할 수 있다. 이미 minK와 maxK가 발견되어 있기 때문에 원래 subarray에 nums[i]를 포함시킨다. 이 때, min(lastMin, lastMax) - start값을 더해야 한다.
- 예를 들어 2 1 3 5 2, minK = 1, maxK = 5라 하자.
- 2 1 3 5가 탐색된 상황에서 2를 추가로 탐색했다. 이 때 [minK와 maxK가 있는 subarray]는 [1 3 5]이다.
- 따라서 2 [1 3 5]로 표현할 수 있다.
- 이 때 2를 추가로 탐색했는데, 이를 [minK와 maxK가 있는 subarray]에 포함하고 그 앞에 있는 element를 순차적으로 포함해야 한다.
- 2 [1 3 5 2] , 그리고 [2 1 3 5 2] 이렇게.
- 간단하게 표현할 수 있다. nums[i]가 minK인 경우 lastMin를 i로 갱신하고, nums[i]가 maxK인 경우 lastMax를 i로 갱신한다.
- lastMin과 lastMax가 둘 다 있는 상황이라면 min(lastMin, lastMax) - start를 한다. 이는 [minK와 maxK가 있는 subarray의 제일 앞의 index] - start를 의미한다.
- 새로 들어온 수(nums[i])가 minK 또는 maxK인 경우 : 이 경우 새로운 subarray를 구성할 수 있다.
위와 같은 pseudo code로 표현할 수 있다. 코드로 나타내면 아-주 간단하다.
// Runtime 114 ms Beats 84.8% // Memory 70.4 MB Beats 57.55% class Solution { public: long long countSubarrays(vector<int>& nums, int minK, int maxK) { long long answer = 0; int lastMax = -1, lastMin = -1, n = nums.size(), start = -1; for(int i = 0; i<n; i++){ if(nums[i] > maxK || nums[i] < minK){ start = i; lastMax = -1; // not found lastMin = -1; // not found } else{ if(nums[i] == minK) lastMin = i; if(nums[i] == maxK) lastMax = i; if(lastMin != -1 && lastMax != -1){ answer += min(lastMax, lastMin) - start; } } } return answer; } };
솔직히 면접에서 이거 물어보면 GG다. 좋은 경험 했다고 생각하자... i번째 index가 array에 추가되었을 때 몇 개의 추가 array가 생기는지를 고민해 보았으면 쉽게 풀렸을 것 같다. ㅠㅠ
시간복잡도
앞에서부터 뒤로 순회만 하니 O(n)이다.
공간복잡도
추가 공간을 사용하지 않으니 O(1)이다.
'PS > PS Log' 카테고리의 다른 글
23.03.06. 풀었던 문제들 (0) | 2023.03.07 |
---|---|
23.03.05. 풀었던 문제들 (0) | 2023.03.06 |
23.03.03. 풀었던 문제들 (0) | 2023.03.03 |
23.03.02. 풀었던 문제들 (0) | 2023.03.02 |
23.03.01. 풀었던 문제들 (0) | 2023.03.01 |