no image
[백준-11000] 강의실 배정
11000번: 강의실 배정 그리디 "활동 선택 문제" 방식의 문제이다. 먼저 시간복잡도부터 살펴보면 1초의 제한시간에 input 의 길이가 20만이다. 따라서 제곱의 복잡도를 가지게 되면 터진다는 걸 알 수 있다. 이 점에 착안해 각 프로세스를 돌면서 log(n) 의 탐색 복잡도 로직으로 같이 이어서 들을 강의를 체킹하여 문제를 풀기로 하였다. 로직은 다음과 같다. 각 프로세스를 "시간 순 → 짧게 끝나는 순" 으로 정렬한다. 정렬한 프로세스를 하나씩 돌면서 나머지 프로세스 중 바로 시작할 수 있는 프로세스를 이분탐색을 통해 찾는다. (반복) 찾은 프로세스들을 모두 checking 해주고 다시 2번으로 돌아간다. 이에 대한 코드는 다음과 같다. #define _CRT_SECURE_NO_WARNINGS #..
2021.08.11
[프로그래머스] 큰수만들기
#include #include #include using namespace std; string solution(string number, int k) { string answer = ""; int window_size = number.size() - k; int max_idx = -1; char max_val; for(int i=0 ; i< window_size ; i++) { max_val = '0'; for(int j = max_idx + 1 ; j
2021.08.11
no image
[백준-1082] 방번호
1028번: 다이아몬드 광산 dp 문제였다. dp 배열에 문자열을 넣고 drop up 방식으로 단순하게 탐색하는 식으로 문제를 풀 수 있었다. 로직은 다음과 같다. dp[1] 부터 차례대로 값을 비교하되 하나의 dp 마다 n개의 수를 넣기 전의 dp 값과 비교하여 해당 수를 추가한 뒤 값이 더 큰 값을 dp 값으로 갱신하는 로직. 말이 좀 어려울 수 있겠지만 dp[i - (수의값)] 에서 해당 수를 첨가하여 커지나 안커지나만 확인하면 되는 문제였다. dp 라 푸는데 좀 오래 걸렸지만.. 다음에는 더 잘 기억하자 #define _CRT_SECURE_NO_WARNINGS #include #include #include #include #include #include #include #include #incl..
2021.08.10
no image
[백준-15684] 사다리 조작
15684번: 사다리 조작 이거는 약간 구현에 신경써야 하는 문제이다. 여러번 풀어보는 것도 나쁘지 않다고 생각한다. 일단 하나씩 다 돌아보는 단순한 브루트포스 알고리즘으로 풀리는 문제이다. 다만 각 가로줄들을 데이터로 잘 표현하여 탐색을 돌리는 구현이 깔끔하게끔 짜야하는 문제가 있다. 각 가로줄을 일일이 저장하는 것이 아닌 특정 점에서 밑으로 내려올 수 있다 없다 로 가로줄을 판단할 수 있다. 예를 들어 check[3][4]=1 이라면 (3,4) 에서 사다리 밑으로 내려올 수 있다는 의미가 될 수 있다. 이를 이용해 check[x][y], check[x][y-1], check[x][y+1] 이 3개의 값을 비교하여 1인 경우 dfs 탐색을 돌리는 식으로 로직을 구성하면 문제를 풀 수 있다. #defin..
2021.08.09
[백준-2805] 나무자르기
2805번: 나무 자르기 간단한 이분탐색 문제이다. 최대값을 right로 0을 left 로 설정한 후 calc 함수만 짠뒤 간단히 이분탐색을 돌리면 풀리는 간단한 문제이다. 다만 값의 범위가 int 값의 범위를 넘어가기 때문에 이를 long long 으로 변환해주어 적절하게 풀면 간단히 풀린다. #define _CRT_SECURE_NO_WARNINGS #include #include #include #include #include #include #include #include #include #include using namespace std; typedef pair pii; typedef tuple tii; typedef long long LL; LL n, m; LL arr[1000001]; LL c..
2021.08.08
no image
[백준-2517] 달리기
[https://www.acmicpc.net/problem/2517] 시간 복잡도가 매우 중요한 문제이다. 일반적인 생각으로 매 선수가 들어올 때마다 앞에서 몇번째인지 찾기 위해서는 "기존 입력을 받는 n" * "자기보다 작은 선수들 찾기 n" 가 되어 n^2 으로 시간이 터져버린다. 때문에 이 문제를 풀기 위해서는 최소한 nlogn 의 로직으로 풀어야 한다. 즉, 이분탐색이나 logn 복잡도의 탐색 알고리즘이 필요하다. 이를 위해 세그먼트 트리를 사용할 수 있다. 로직은 다음과 같다. 첫번째 선수 하나 들어왔을 때 세그먼트 트리에 체크해준다. 두 번째 선수 부터는 세그먼트 트리에 업데이트 하기전 자기보다 작은 값까지 몇까지 있는지 logn의 복잡도로 확인한다. 위 1, 2 번 방법을 반복한다. 즉, 세..
2021.08.08
no image
[백준-1202] 보석도둑
https://www.acmicpc.net/problem/1202 1202번: 보석 도둑 첫째 줄에 N과 K가 주어진다. (1 ≤ N, K ≤ 300,000) 다음 N개 줄에는 각 보석의 정보 Mi와 Vi가 주어진다. (0 ≤ Mi, Vi ≤ 1,000,000) 다음 K개 줄에는 가방에 담을 수 있는 최대 무게 Ci가 주어진다. (1 ≤ Ci www.acmicpc.net 위 문제의 경우 1개의 우선순위큐를 통해 구할 수 있다. 이런 그리디 문제가 나는 비슷한 문제가 많으니 기억해두자. 일단 입력 받은 가방과 보석을 모두 오름차순으로 정렬 한다. 그 후 작은 가방 하나씩 꺼내고, 보석 배열을 순회하면서 집어넣을 수 있는 보석을 우선순위 큐에 집어 넣는다. (by while) 다 집어 넣었으면 우선순위 큐의..
2021.07.13
[비전6] Corners
◆1. Interest Points and Corners 일단 용어에 대해 잡고 넘어가자. Interest points 는 keypoints 를 뜻하고 이것들은 보통 feature 라고도 불리우기도 한다. 원래 이렇게 부르는게 아니지만 비전 공학 특성 상 뭐가 정확히 정의되어있다기 보다는 자기 편하게 부르기 나름이기 때문에 그냥 이렇게 알고 넘어가자. Correspondence Correspondence 란 매치되는 이미지를 이야기한다. 보통 두 이미지를 비교할 때, 이거를 kernel information 으로 사용한다. 보통 matching points, patches, edges, regions across images 를 뜻한다. Fundamental matrix 어떻게 매칭할 지에 사용한다. 다..
2021.05.03
no image
[비전5] Frequency and Image
◆1. Non-Linear Filters 이미지의 주파수에 대해서 알아보자. 픽셀 데이터로 되어있는 이미지를 픽셀의 변화 값에 따른 frequency 변화 값을 따로 뺄 수 있다. 이를 주파수라고 하면 해당 주파수 만을 뽑아내서 주파수 도메인으로 확인해볼 수 있다. 이 때 사용되는 변환 방법으로 푸리에 변환(Fourier transform )이 있다. 푸리에 변환을 통해 이미지를 변환하게 되면 보통 각 frequency의 Coefficient 값으로 표현한다. 이미지를 위의 그래프와 같이 각 주파수에 따른 Coefficient 만 남겨두는 식으로 압축(?!)해서 저장해둘 수가 있다. 또한 FFT의 교환법칙을 통해 CNN 에서의 for 문 3개를 쓴 세제곱 복잡도를 줄일 수 있다는 장점도 가지고 있다. 일..
2021.05.03
11000번: 강의실 배정

그리디 "활동 선택 문제" 방식의 문제이다.

먼저 시간복잡도부터 살펴보면 1초의 제한시간에 input 의 길이가 20만이다. 따라서 제곱의 복잡도를 가지게 되면 터진다는 걸 알 수 있다.

이 점에 착안해 각 프로세스를 돌면서 log(n) 의 탐색 복잡도 로직으로 같이 이어서 들을 강의를 체킹하여 문제를 풀기로 하였다.

로직은 다음과 같다.

  1. 각 프로세스를 "시간 순 → 짧게 끝나는 순" 으로 정렬한다.
  2. 정렬한 프로세스를 하나씩 돌면서 나머지 프로세스 중 바로 시작할 수 있는 프로세스를 이분탐색을 통해 찾는다. (반복)
  1. 찾은 프로세스들을 모두 checking 해주고 다시 2번으로 돌아간다.

이에 대한 코드는 다음과 같다.

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
#include <cstdio>
#include <tuple>
#include <string>
#include <string.h>
#include <stack>
#include <set>
#include <map>


using namespace std;
typedef long long LL;
typedef pair<int, int> pii;

int check[200001];
bool compare(pii a, pii b) {
	if (a.first == b.first) {
		return a.second < b.second;
	}
	else {
		return a.first < b.first;
	}
}

int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);
	
	int n, a, b;
	cin >> n;
	vector<pii> cls;
	for (int i = 0; i < n; i++) {
		cin >> a >> b;
		cls.push_back({ a,b });
	}
	sort(cls.begin(), cls.end(), compare);

	vector<int> idx;
	for (int i = 0; i < cls.size(); i++)
		idx.push_back(cls[i].first);

	int res = 0;
	for (int i = 0; i < cls.size(); i++) {
		if (check[i] != 0) continue;
		else check[i] = 1;

		int cx = cls[i].first;
		int cy = cls[i].second;

		while (1) {
			// 제일 맞는 놈 바로 체킹 -> log(n)
			int id = lower_bound(idx.begin(), idx.end(), cy) - idx.begin();
			while (check[id] != 0) 
				id += 1;
			if (id > cls.size() - 1 || cls[id].first < cy) 
				break;
			check[id] = 1;
			cy = cls[id].second;
		}
		res += 1;
	}
	cout << res;
	return 0;
}

'책장 > 알고리즘' 카테고리의 다른 글

[백준-1918] 후위 표기식  (1) 2021.08.11
[백준-20040] 사이클 게임  (0) 2021.08.11
[프로그래머스] 큰수만들기  (0) 2021.08.11
[백준-1082] 방번호  (0) 2021.08.10
#include <string>
#include <vector>
#include <algorithm>

using namespace std;

string solution(string number, int k) {
    string answer = "";
    int window_size = number.size() - k;

    int max_idx = -1;
    char max_val;
    for(int i=0 ; i< window_size ; i++) {
        max_val = '0';
        for(int j = max_idx + 1 ; j<i+k+1 ; j++) {
            if(max_val < number.at(j)) {
                max_val = number.at(j);
                max_idx = j;
            }
        }
        answer.push_back(max_val);
    }    
    return answer;
}

 

i번째의 수를 고를 때, k 개를 뽑아야 되니까 최소 i부터 k+1 범위에 있는 수중에서 큰 수만을 고르게 되면 결국 전체적으로 봤을 때 최대 값이 생기게 된다.

그리디한 방법 하에, answer 는 앞자리 수가 크면 클수록 가중치가 크기 때문에 앞에서부터 뽑힐 수 있는 모든 후보군 중에서 큰 값을 뽑게 되면 결국 제일 큰 값이 처리되기 때문에 답이 나온다.

 

쉽게 설명하자면 각 자리수의 수를 확인할 수 있는 윈도우를 설정하고, 해당 윈도우에서 가장 큰 값을 체킹하고 answer 에 넣어준다. 그렇게 되면 확실하게 k 개를 뺀 개수가 나오게 되고 가장 큰 값이 답으로 나오게 된다.

 

'책장 > 알고리즘' 카테고리의 다른 글

[백준-20040] 사이클 게임  (0) 2021.08.11
[백준-11000] 강의실 배정  (0) 2021.08.11
[백준-1082] 방번호  (0) 2021.08.10
[백준-15684] 사다리 조작  (0) 2021.08.09
1028번: 다이아몬드 광산

 

dp 문제였다.

dp 배열에 문자열을 넣고 drop up 방식으로 단순하게 탐색하는 식으로 문제를 풀 수 있었다.

 

로직은 다음과 같다.

dp[1] 부터 차례대로 값을 비교하되 하나의 dp 마다 n개의 수를 넣기 전의 dp 값과 비교하여 해당 수를 추가한 뒤 값이 더 큰 값을 dp 값으로 갱신하는 로직.

말이 좀 어려울 수 있겠지만 dp[i - (수의값)] 에서 해당 수를 첨가하여 커지나 안커지나만 확인하면 되는 문제였다.

dp 라 푸는데 좀 오래 걸렸지만.. 다음에는 더 잘 기억하자

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
#include <cstdio>
#include <tuple>
#include <string>
#include <string.h>
#include <stack>
#include <set>
#include <map>


using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
typedef tuple<int, int, string> tii;
string dp[200];

string convert(string a) {
	string res;
	if (a == "") return "";
	for (int i = 0; i < a.size(); i++) {
		if (a.at(i) == '0') continue;
		else {
			res = a.substr(i);
			break;
		}
	}
	if (res == "") return "0";
	else return res;
}
int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);
	
	int n, input, money;
	vector<int> v;
	int _min = 50;
	cin >> n;
	for (int i = 0; i < n; i++) {
		cin >> input;
		v.push_back(input);
		_min = min(_min, input);
	}
	cin >> money;

	for (int i = _min; i <= money; i++) {
		for (int j = 0; j < n; j++) {
			if (i - v[j] < 0) continue;
			for (int k = 0; k < dp[i - v[j]].size(); k++) {
				if (dp[i - v[j]][k] - '0' >= j) continue;
				string new_str = convert(dp[i - v[j]].substr(0, k) + to_string(j) + dp[i - v[j]].substr(k + 1));
				// cout << new_str << endl;
				if (dp[i].size() < new_str.size()) {
					dp[i] = new_str;
				}
				else if (dp[i].size() == new_str.size()) {
					dp[i] = max(dp[i], new_str);
				}
			}
			if (dp[i - v[j]].empty() && j == 0)
				continue;

			string t = dp[i - v[j]] + to_string(j);
			if (dp[i].length() < t.length())
				dp[i] = t;
			else if (dp[i].length() == t.length())
				dp[i] = max(dp[i], t);
		}
	}

	if (dp[money].empty()) cout << 0;
	else {
		cout << dp[money];
	}
	return 0;
}

 

 

'책장 > 알고리즘' 카테고리의 다른 글

[백준-11000] 강의실 배정  (0) 2021.08.11
[프로그래머스] 큰수만들기  (0) 2021.08.11
[백준-15684] 사다리 조작  (0) 2021.08.09
[백준-2805] 나무자르기  (0) 2021.08.08

15684번: 사다리 조작

이거는 약간 구현에 신경써야 하는 문제이다.

여러번 풀어보는 것도 나쁘지 않다고 생각한다.

일단 하나씩 다 돌아보는 단순한 브루트포스 알고리즘으로 풀리는 문제이다. 다만 각 가로줄들을 데이터로 잘 표현하여 탐색을 돌리는 구현이 깔끔하게끔 짜야하는 문제가 있다.

각 가로줄을 일일이 저장하는 것이 아닌 특정 점에서 밑으로 내려올 수 있다 없다 로 가로줄을 판단할 수 있다.

예를 들어 check[3][4]=1 이라면 (3,4) 에서 사다리 밑으로 내려올 수 있다는 의미가 될 수 있다.

이를 이용해 check[x][y], check[x][y-1], check[x][y+1] 이 3개의 값을 비교하여 1인 경우 dfs 탐색을 돌리는 식으로 로직을 구성하면 문제를 풀 수 있다.

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>

using namespace std;

int n, m, h; 
int arr[100][100];
int check[100][100];
int _min = 1000000;
bool isok() {
    int cx;
    for (int i = 1; i <= n; i++) {
        cx = i;
        for (int j = 1; j <= h; j++) {
            if (check[j][cx] == 1) cx = cx + 1;
            else if (check[j][cx-1] == 1) cx = cx - 1;
        }
        if (cx != i) return false;
    }
    return true;
}
void dfs(int x, int cnt) {
    if (cnt >= 4) {
        return;
    }
    if (isok()) {
        _min = min(_min, cnt);
        return;
    }

    for (int i = x; i <= h; i++) {
        for (int j = 1; j < n; j++) {
            // check[x][y] = 1 
            if (check[i][j] == 1) continue;
            if (check[i][j-1] == 1) continue;
            if (check[i][j+1] == 1) continue;

            check[i][j] = 1;
            dfs(i, cnt + 1);
            check[i][j] = 0;
        }
    }
}
int main() {
    int a, b;

    cin >> n >> m >> h;
    for (int i = 0; i < m; i++) {
        cin >> a >> b;
        check[a][b] = 1;
    }
    dfs(1, 0); 
    if (_min == 1000000) cout << -1;
    else cout << _min;

    return 0;
}

'책장 > 알고리즘' 카테고리의 다른 글

[프로그래머스] 큰수만들기  (0) 2021.08.11
[백준-1082] 방번호  (0) 2021.08.10
[백준-2805] 나무자르기  (0) 2021.08.08
[백준-2517] 달리기  (0) 2021.08.08

2805번: 나무 자르기

간단한 이분탐색 문제이다.

최대값을 right로 0을 left 로 설정한 후 calc 함수만 짠뒤 간단히 이분탐색을 돌리면 풀리는 간단한 문제이다.

다만 값의 범위가 int 값의 범위를 넘어가기 때문에 이를 long long 으로 변환해주어 적절하게 풀면 간단히 풀린다.

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
#include <cstdio>
#include <tuple>
#include <string>
#include <string.h>
#include <stack>
#include <set>


using namespace std;
typedef pair<int, int> pii;
typedef tuple<int, int, string> tii;
typedef long long LL;

LL n, m;
LL arr[1000001];

LL calc(LL x) {
    LL res = 0;
    for (int i = 0; i < n; i++) {
        res += (arr[i] - x > 0) ? arr[i] - x : 0;
    }
    return res;
}
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);

    cin >> n >> m;

    LL right = 1000000000;
    LL left= 0;
    for (int i = 0; i < n; i++) {
        cin >> arr[i];
    }

    LL mid;
    LL ans = 0;
    while (left <= right) {
        mid = (left + right) / 2;
        LL res = calc(mid);
        if (res >= m) {
            ans = max(ans, mid);
            left = mid + 1;
        }
        else if(res < m){
            right = mid - 1;
        }
    }

    cout << ans;

    return 0;
}

'책장 > 알고리즘' 카테고리의 다른 글

[백준-1082] 방번호  (0) 2021.08.10
[백준-15684] 사다리 조작  (0) 2021.08.09
[백준-2517] 달리기  (0) 2021.08.08
[백준-1202] 보석도둑  (0) 2021.07.13

[https://www.acmicpc.net/problem/2517]

시간 복잡도가 매우 중요한 문제이다. 일반적인 생각으로 매 선수가 들어올 때마다 앞에서 몇번째인지 찾기 위해서는 "기존 입력을 받는 n" * "자기보다 작은 선수들 찾기 n" 가 되어 n^2 으로 시간이 터져버린다.

때문에 이 문제를 풀기 위해서는 최소한 nlogn 의 로직으로 풀어야 한다. 즉, 이분탐색이나 logn 복잡도의 탐색 알고리즘이 필요하다. 이를 위해 세그먼트 트리를 사용할 수 있다.

로직은 다음과 같다.

  1. 첫번째 선수 하나 들어왔을 때 세그먼트 트리에 체크해준다.
  2. 두 번째 선수 부터는 세그먼트 트리에 업데이트 하기전 자기보다 작은 값까지 몇까지 있는지 logn의 복잡도로 확인한다.
  3. 위 1, 2 번 방법을 반복한다.

즉, 세그먼트 트리에는 해당 값이 있는지 1로 체크만 해주고, 이에 따라 부분 합의 의미는 해당 범위까지 선수가 몇명있는지의 의미이다. 이를 통해 logn의 복잡도로 탐색할 수 있으며, 결국 nlogn 의 복잡도로 문제를 풀 수 있게 된다.

  • 코드
#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <vector> 
#include <algorithm> 
#include <queue> 
#include <cstdio>
#include <tuple> 
#include <string> 
#include <string.h> 
#include <stack> 
#include <set>

using namespace std;
typedef long long LL;
typedef pair<LL, LL> pii;
typedef tuple<int, int, string> tii;


LL n, m;
LL a, b;
vector<pii> v;
int tree[2000000];

int query(int node, int s, int e, int l, int r) {
    if (l > e || r < s) return 0;
    if (l <= s && e <= r) return tree[node];
    else {
        return query(node * 2, s, (s+e)/2, l, r) + query(node * 2 + 1, (s + e) / 2+1, e, l, r);
    }
}

void update(int node, int s, int e, int idx, int v) {
    if (idx < s || e < idx) return;
    if (s == e) tree[node] = v;
    else {
        update(node * 2, s, (s + e) / 2, idx, v);
        update(node * 2 + 1, (s + e) / 2 + 1, e, idx, v);
        tree[node] = tree[node * 2] + tree[node * 2 + 1];
    }
}

bool compare(pii a, pii b) {
    return a.second  < b.second;
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);

    cin >> n;
    for (LL i = 0; i < n; i++) {
        cin >> a;
        v.push_back({ i,a });
    }

    int res = 0;
    sort(v.begin(), v.end(), compare);
    for (int i = 0; i < v.size(); i++) {
        v[i].second = ++res;
    }

    sort(v.begin(), v.end());

    for (int i = 0; i < v.size(); i++) {
        int cnt = 0;
        int cur_power = v[i].second;

        if (cur_power > 1) {
            // 제일 작은 원소가 아니라면
            cnt = query(1, 1, res, 1, cur_power - 1);
        }
        update(1, 1, res, cur_power, 1);
        printf("%d\n", i + 1 - cnt);
    }


    return 0;
}
```

'책장 > 알고리즘' 카테고리의 다른 글

[백준-15684] 사다리 조작  (0) 2021.08.09
[백준-2805] 나무자르기  (0) 2021.08.08
[백준-1202] 보석도둑  (0) 2021.07.13
[프로그래머스]타겟넘버  (0) 2021.04.30

https://www.acmicpc.net/problem/1202

 

1202번: 보석 도둑

첫째 줄에 N과 K가 주어진다. (1 ≤ N, K ≤ 300,000) 다음 N개 줄에는 각 보석의 정보 Mi와 Vi가 주어진다. (0 ≤ Mi, Vi ≤ 1,000,000) 다음 K개 줄에는 가방에 담을 수 있는 최대 무게 Ci가 주어진다. (1 ≤ Ci

www.acmicpc.net

위 문제의 경우 1개의 우선순위큐를 통해 구할 수 있다. 이런 그리디 문제가 나는 비슷한 문제가 많으니 기억해두자.

일단 입력 받은 가방과 보석을 모두 오름차순으로 정렬 한다.

그 후 작은 가방 하나씩 꺼내고, 보석 배열을 순회하면서 집어넣을 수 있는 보석을 우선순위 큐에 집어 넣는다. (by while) 다 집어 넣었으면 우선순위 큐의 top 에 위치하는 보석을 해당 가방에 넣는 것으로 확정하고 그 다음 가방을 똑같은 방법으로 확인하는 로직이다. 말이 쉽지 직접 구현하는 건 좀 어렵다.

 

#define _CRT_SECURE_NO_WARNINGS
#include <cstdio>
#include <algorithm>
#include <vector>
#include <queue>
#include <iostream>

using namespace std;
typedef long long LL;
typedef pair<LL, LL> pii;
int n, k;

int main()
{
    ios_base::sync_with_stdio(0);

    cin.tie(0); //cin 실행속도 향상

    scanf("%d %d", &n, &k);
    vector<pii> jewl;
    vector<LL> bag;
    priority_queue<LL> pq;

    LL m, v, t;
    for (int i = 0; i < n; i++)
    {
        cin >> m >> v;
        jewl.push_back({m, v});
    }

    for (int i = 0; i < k; i++)
    {
        cin >> t;
        bag.push_back(t);
    }
    sort(bag.begin(), bag.end());
    sort(jewl.begin(), jewl.end());

    long long answer = 0;
    int idx = 0;
    for (int i = 0; i < k; i++)
    {
        while (idx < n && jewl[idx].first <= bag[i])
        {
            // 가방에 넣을 수 있는지, 없는지 확인
            pq.push(jewl[idx++].second);
        }
        if (!pq.empty())
        {
        	// 우선순위 큐의 top에 있는 놈이 정답
            answer += pq.top();
            pq.pop();
        }
    }

    cout << answer;

    return 0;
}

'책장 > 알고리즘' 카테고리의 다른 글

[백준-2805] 나무자르기  (0) 2021.08.08
[백준-2517] 달리기  (0) 2021.08.08
[프로그래머스]타겟넘버  (0) 2021.04.30
완전탐색  (0) 2021.04.24

 

◆1. Interest Points and Corners

일단 용어에 대해 잡고 넘어가자. Interest points 는 keypoints 를 뜻하고 이것들은 보통 feature 라고도 불리우기도 한다.

원래 이렇게 부르는게 아니지만 비전 공학 특성 상 뭐가 정확히 정의되어있다기 보다는 자기 편하게 부르기 나름이기 때문에 그냥 이렇게 알고 넘어가자.

 

Correspondence

Correspondence 란 매치되는 이미지를 이야기한다. 보통 두 이미지를 비교할 때, 이거를 kernel information 으로 사용한다.

보통 matching points, patches, edges, regions across images 를 뜻한다.

 

Fundamental matrix

어떻게 매칭할 지에 사용한다. 다른 각도로 checking하면서 비교할 수 있기 위해 사용하는 놈이다.

다음 그림을 보자. sns 에 올려진 사진을 모아 fundamental matrix 로 correspondence 를 계산하고 어디서 찍었는지 확인할 수도 있다.

 

Feature points 사용

Image alignment 에 사용되기도 하고, 3D reconstruction, Motion tracking, Robot navigation, Indexing and database retrieval 에도 사용된다. 음.. 이미지에 특징점을 잡고 해당 데이터를 연산에 사용하는 모든 분야를 생각하면 될 거같다.

 

Panorama

갤럭시를 사용하면 Panorama 카메라가 있을 수 있다. 해당 기능을 구현하기 위해서는 여러장의 이미지를 찍은다음에 해당 이미지들의 특징점들을 잡고, correspondence 를 계산하여 이어 붙이는 로직을 구현해주어야 한다.

 

정확하게는 해당 특징 점을 vector 형태로 나타낸 feature descriptor 를 뽑아내고 각 vector 의 거리를 계산하면서 correspondence 를 찾는 과정을 거친다고 보면 될 것 같다.

 

Characteristics of good features

좋은 feature 의 특징은 다음과 같다.

  • Repeatability : 같은 특징은 반복된다.
  • Saliency : 각 feature 들은 구별된다.
  • Compactness and efficiency : 전체 이미지 픽셀 개수보다는 적은 개수의 feature 이어야 한다.
  • Locality : 특정 위치를 대표해야 한다.

 

 

◆2. Corner Detection

 

Mathematics

그러면 좋은 feature 들을 어떻게 계산해서 구할까?

위의 식처럼 주변의 픽셀 값과 계산해서 해당 픽셀이 도드라지는지 확인한다. 이 식으로 계산하게 되면 다음과 같은 input, output 을 알 수 있게 된다.

 

해당 output 을 보고 어느 부분이 좋은 feature 인지 아닌지 알 수 있다.

위의 그림을 봐보자.

특정 이미지에서 b,c,d 에 해당하는 영역에 correspondence 를 계산한 결과이다. (b)의 경우 center 부분이 나머지 픽셀들과 도드라지게 다른 것을 확인할 수 있고, (c) 는 비슷한 픽셀 데이터들이 line 처럼 존재하는 걸 확인할 수 있으며, (d) 는 그냥 전체적으로 비슷비슷하다는 것을 확인할 수 있다.

그러면 셋 중에 어떤 것이 좋은 feature 일까? 당연히 (b) 이다. 이건 그냥 넘어가겠다.

 

여튼 저런 식으로 corner feature 를 뽑아낼 수 있는데

식을 다음과 같이 좀더 줄여서 사용할 수 있다.

 

Corner response function

고유값과 고유벡터를 통해 구한다는데 정확한 것은 추가 검색을 하도록 하자..

 

 

Harris corner detector

위의 Corner response function 를 사용해서 corner 를 구하는 detector 이다. corner 를 구하는 방식은 다음과 같다.

 

 

'책장 > Computer Vision' 카테고리의 다른 글

[비전5] Frequency and Image  (1) 2021.05.03
[비전4] Edge  (0) 2021.04.20
[비전3] filters  (0) 2021.04.20
[비전2] linear algebra  (0) 2021.04.20

◆1. Non-Linear Filters

 

이미지의 주파수에 대해서 알아보자.

픽셀 데이터로 되어있는 이미지를 픽셀의 변화 값에 따른 frequency 변화 값을 따로 뺄 수 있다. 이를 주파수라고 하면 해당 주파수 만을 뽑아내서 주파수 도메인으로 확인해볼 수 있다. 이 때 사용되는 변환 방법으로 푸리에 변환(Fourier transform )이 있다.

 

푸리에 변환을 통해 이미지를 변환하게 되면 보통 각 frequency의 Coefficient 값으로 표현한다.

이미지를 위의 그래프와 같이 각 주파수에 따른 Coefficient 만 남겨두는 식으로 압축(?!)해서 저장해둘 수가 있다. 또한 FFT의 교환법칙을 통해 CNN 에서의 for 문 3개를 쓴 세제곱 복잡도를 줄일 수 있다는 장점도 가지고 있다.

 

일반적으로 영상처리 비전 쪽에서 이 푸리에 변환은 영상을 개선하는데 주로 많이 쓰인다.

노이즈가 있는 입력영상의 푸리에 변환 이미지

 

이와 같이 푸리에 변환은 영상을 개선하기 위해 노이즈 처리하기 힘든 입력 영상을 노이즈 처리하기에 좋은 주파수영역으로 바꾼 다음 노이즈를 제거하는 역할을 수행하게 된다.

위 이미지의 오른쪽 이미지는 푸리에 변환을 수행한 spectrum magnitude 이미지이다. 이 주파수 영역을 나타내는 스펙트럼 이미지에서 입력 영상의 노이즈 부분에 해당하는, 즉 고주파 영역이나 저주파 영역에 해당하는 부분을 아래와 같이 제거해 준다면 개선된 영상을 얻을 수 있다.

노이즈 영역을 제거한 스펙트럼 영상

이렇게 스펙트럼 영상에서 노이즈 영역에 해당하는 부분을 제거하고 푸리에 역변환(IDFT)을 수행하여 원래의 입력 영상 도메인으로 바꾸면 개선된 이미지를 얻을 수 있게 된다. 푸리에 변환에 의한 필터링 과정은 다음과 같다.

이러한 필터링 과정에서 H(u, v) 부분을 구현할 때 유의해야 할 점은 F(u, v)와 같은 크기의 영상 또는 행렬로 생성하지 않고, F(u, v)의 u, v 에 따라 H(u, v)의 스칼라 값을 계산하여 F(u, v)에 곱하는 방식을 사용하게 된다.

 

이러한 H(u, v) 부분에 적용하여 구현 할 수 있는 필터는 다음과 같은 필터가 있다.

 

ⓐ 저주파 통과 필터 (Low-pass filter)

저주파 통과 필터는 F(u, v)의 저주파 영역은 통과시키고 고주파 영역은 0으로 만들어 통과시키지 않는 필터를 말한다. 영상에서 잡음을 제거하거나 또는 약화시키고 블러링하여 에지 등의 세밀한 부분을 부드럽게 만드는 역할을 한다.

 

ⓑ 고주파 통과 필터 (High-pass filter)

고주파 통과 필터는 F(u, v)의 고주파 영역은 통과시키고 저주파 영역은 0으로 만들어 통과시키지 않는 필터를 말한다. 고주파 통과 필터는 영상을 날카롭게 강조하는 Sharpenning 효과를 일으킨다. 입력 영상에서는 고주파 통과 필터링을 하여 IDFT 하면 변화가 없는 영역은 0의 값을, 변화가 심한 에지 영역은 양수 또는 음수의 값을 갖게 된다.

 

퓨리에 변환의 목표는 어떻게 영상을 저주파 성분으로 조그맣게 두었따가 고주파 성분으로 신호를 확대시켰을 때 신호가 제대로 남아있을까? 를 해결하는 것이다.

 

 

Fourier Bases

위에서 말했듯이 이미지를 Fourier Transform 하여 Coefficient 값만 남겨둔다고 했었다. 그러면 해당 Coefficient 에 해당하는 주파수는 어떻게 저장하나? 에 대한 답이 Fourier Bases 이다.

기본적인 주파수 Bases 를 따로 두어 Coefficient 데이터에 계산해주는 식으로 이미지를 복원시킬 수 있다.

 

 

Edit frequencies

보이는 그림처럼 fourier 변환하여 뽑아낸 Fourier d

ecomposition image 를 조작하여 원래 이미지를 다룰 수 있다. 위에서 말한 Low-pass filter 와 High-pass filter 도 이 decomposition image 로 다룰 수 있다.

다만 이 정도는 알아두자. 저 decomposition image 에서 가운데 원 부분이 low frequency를 나타내는 coefficient 이고, 외각으로 갈수록 high frequency 를 나타내는 coefficient 라는 것이다.

예를 들어 low frequency를 버린다는 말은 면을 거른다는 말이고, high frequency를 버린다는 말은 edge 를 버린다는 말이다.

 


Amplitude & Phase

저 decomposition image 는 푸리에 변환 이후의 amplitude 를 의미한다.

우측의 Phase 는 이미지의 '패턴' 을 의미한다. 보통 모공과 같은 정확한 정보를 위해서는 '패턴'을 정보화 한 phase 를 사용하는데 많이 쓰지는 않는다.

정리해서 Amptitude 가 더 사람이 이해하기 직관적인 info 이고, Phase 는 직관적인 정보 보다는 정확한 정보로써 패턴을 정보화한 정보구나~ 라는 것을 알고 있으면 될 듯 싶다.

 


Edge Artifact Problem

위의 가우시안 필터와 Box filter 두 가지를 통해 이미지 blur 처리를 했을 때, box filter 는 이상한 edge들이 있는 것을 확인할 수 있다. 왜 그럴까?

 

FT 로 설명할 수 있다. FT 는 위에서 말했듯이 교환 법칙 및 결합 법칙이 가능해서 CNN 의 복잡도를 줄일 때 사용할 수 있다고 했다.

 

Box filter 를 FT 하게 되면

다음과 같이 갑자기 변하는 경계면 때문에 edge artifact 가 생기게 된다.

따라서 ((IMAGE * FT) * (BoxFilter*FT)) * IFT = BlurIMAGE 에서 edge artifact 가 계속 영향을 끼치게 된다. 때문에 Box filter 로 blur 처리했을 시 edge artifact 에 의해 이상한 edge 들이 있는 것을 확인할 수 있다.

 


Aliasing problem

이미지 scale 을 줄였을 때 high frequency component 를 잃는 것을 Aliasing problem 이라고 한다.

허나 이렇게 말하면 직관적으로 어떤 problem 인지 확 와 닿지가 않는다. 예를 들어 동영상을 찍을 때, 자동차의 바퀴 회전이 뒤로 가는(?!) 모습으로 찍힌 것을 볼 수 있다. 이를 Aliasing problem 이라고 한다. 즉, 영상의 frame 주파수가 실제 해당 물체의 frequency를 따라가지 못해 생기는 문제이다.

이를 해결하기 위해 어떻게 해야할까?

 

 

Nyquist-Shannon Sampling Theorem

2가지의 해결 방법이 있다.

  1. Sampling more!: 영상을 찍는다면 사진기가 찍는 frame 주파수 성능이 실제 물체의 주파수 2배 이상이 되도록 한다.
  1. 원본을 blur 하게 만든다.: 사진기를 바꿀 수 없다면 아예 실제 물체의 주파수를 blur 처리하여 줄여버린다.

 

 


참고링크

 

'책장 > Computer Vision' 카테고리의 다른 글

[비전6] Corners  (0) 2021.05.03
[비전4] Edge  (0) 2021.04.20
[비전3] filters  (0) 2021.04.20
[비전2] linear algebra  (0) 2021.04.20