문제 : https://www.acmicpc.net/problem/2529


위상정렬 백트레킹 부르트포스

1) 백트레킹, 부르트포스를 이용한 풀이

 K가 최대 9이므로, 모든 숫자 조합을 만들어서 다 검사하는 백트레킹 + 부르트포스 방법을 생각했다. 복잡도는 O((K+1) * (K+1)!) 으로 K가 최대여도 시간 초과 문제에 걸리지 않는다.

● 백트레킹을 이용해 K + 1 개의 숫자 조합을 만든다. 이때, 백트레킹을 가장 작은 수부터 숫자 조합을 만들게 구현한다.

● 숫자 조합이 만들어지면 부등호에 맞는 숫자인지 checkNumber 함수를 이용해 체크한다.

● 우리는 백트레킹을 가장 작은 수부터 만들게 구현했으므로, 첫 번째로 부등호에 맞는 숫자가 가장 작은 값이 되므로 가장 작은 값으로 저장하고, 그 뒤로는 값이 점점 더 커지므로 큰 값으로 계속 저장해준다.

#include<iostream>
#include<vector>
#include<algorithm>
#include<string>

using namespace std;

int k;
string ans1, ans2;
bool checkS, chk[10];
char op[10];

bool checkNumber(vector<char> v) {
	for (int i = 0; i < k; i++){
		if (op[i] == '<') {
			if (v[i] >= v[i + 1]) {
				return false;
			}
		}
		else if (op[i] == '>') {
			if (v[i] <= v[i + 1]) {
				return false;
			}
		}
	}
	return true;
}

void BT(vector<char> v) {
	if (v.size() == k + 1) {
		bool flag = checkNumber(v);
		if (!flag)
			return;
		string temp = {};
		for (int i = 0; i < v.size(); i++)
			temp += v[i];
		if (checkS == false){ // 첫 가장 작은 값 저장.
			checkS = true;
			ans1 = temp;
		}
		ans2 = temp;
		return;
	}
	for (int i = 0; i < 10; i++) {
		if (chk[i] == false) {
			chk[i] = true;
			v.push_back(char(i + '0'));
			BT(v);
			v.pop_back();
			chk[i] = false;
		}
	}
}

int main(void) {
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);

	cin >> k;
	for (int i = 0; i < k; i++)
		cin >> op[i];

	vector<char> v;
	BT(v);

	cout << ans2 << '\n' << ans1;
	return 0;
}

2) 위상정렬을 이용한 풀이

 1번 풀이로 시간 초과를 직면하지는 않았지만, 다른 분들 채점 현황을 보니 더 좋은 알고리즘이 있는 것 같아 고민하다가 위상정렬을 이용한 알고리즘을 발견했다.

● 가장 작은 값을 찾을 때는, 작은 숫자부터 앞쪽에 배치해야하므로 A < B 상태가 오면 A -> B 로 간선을 주고 inDegree가 0인 노드 중 가장 번호가 작은(앞쪽인) 노드부터 남은 숫자 중 가장 작은 값을 줍니다.

● 가장 큰 값을 찾을 때는, 큰 숫자부터 앞쪽에 배치해야하므로 A < B 면 A <- B 로 간선을 주고, 마찬가지로 inDegree가 0인 노드 중 가장 번호가 작은(앞쪽인) 노드부터 남은 숫자 중 가장 큰 값을 줍니다.

#include<iostream>
#include<vector>
#include<cstring>
#include<queue>
using namespace std;

int main(void) {
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);

	int k, inDegree[10] = {}, revInDegree[10] = {};
	vector<int> map[10], revMap[10];
	char ans1[11] = {}, ans2[11] = {};
	char temp;
	cin >> k;
	for (int i = 0; i < k; i++){
		cin >> temp;
		if (temp == '<') { // i 에서 i + 1로 
			map[i].push_back(i + 1);
			inDegree[i + 1]++;
			revMap[i + 1].push_back(i);
			revInDegree[i]++;

		}
		else {
			map[i + 1].push_back(i);
			inDegree[i]++;
			revMap[i].push_back(i + 1);
			revInDegree[i + 1]++;
		}
	}
	// 가장 작은 값 부터 찾자.
	priority_queue<int, vector<int>, greater<int>> pq;
	bool check[10] = {}; // 사용한 값 저장.
	for (int i = 0; i <= k; i++)
		if (inDegree[i] == 0)
			pq.push(i);
	while (!pq.empty()) {
		int curNode = pq.top(); pq.pop();
		for (int i = 0; i < 10; i++)
			if (check[i] == false) {
				ans1[curNode] = char(i + '0');
				check[i] = true;
				break;
			}
		for (int i = 0; i < map[curNode].size(); i++) {
			inDegree[map[curNode][i]]--;
			if (inDegree[map[curNode][i]] == 0)
				pq.push(map[curNode][i]);
		}
	}
	// 가장 큰 값을 찾자.
	memset(check, false, sizeof(check));
	for (int i = 0; i <= k; i++)
		if (revInDegree[i] == 0)
			pq.push(i);
	while (!pq.empty()) {
		int curNode2 = pq.top(); pq.pop();
		for (int i = 9; i >= 0; i--){
			if (check[i] == false) {
				ans2[curNode2] = char(i + '0');
				check[i] = true;
				break;
			}
		}
		for (int i = 0; i < revMap[curNode2].size(); i++) {
			revInDegree[revMap[curNode2][i]]--;
			if (revInDegree[revMap[curNode2][i]] == 0)
				pq.push(revMap[curNode2][i]);
		}
	}
	cout << ans2 << '\n' << ans1;
	return 0;
}

 

문제 : https://www.acmicpc.net/problem/10868


세그트리

자료구조 세그트리를 이용하는 전형적인 문제로, 구간의 최솟값을 가질 수 있게 세그트리를 만들어주면 됩니다.

#include<iostream>
#include<algorithm>

using namespace std;

int N[100000] = {};
int tree[400000] = {};

int init(int index, int start, int end) {
	int mid = (start + end) / 2;

	if (start == end)
		return tree[index] = N[start];
	else
		return tree[index] = min(init(2 * index + 1, start, mid), init(2 * index + 2, mid + 1, end));
}

// 제일 작은 값을 찾자.
int get_min(int index, int start, int end, int left, int right) {
	int mid = (start + end) / 2;

	if (end < left || right < start){
		return 1987654321;
	}
	if (left <= start && end <= right){
		return tree[index];
	}

	return min(get_min(2 * index + 1, start, mid, left, right), get_min(2 * index + 2, mid + 1, end, left, right));
}


int main(void) {
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);

	int n, m, a, b;
	long long ans = 0;
	
	cin >> n >> m;

	for (int i = 0; i < n; i++)
		cin >> N[i];

	init(0, 0, n - 1);

	for (int i = 0; i < m; i++) {
		cin >> a >> b;
		cout << get_min(0, 0, n - 1, a - 1, b - 1) << '\n';
	}

}

 

문제 : https://www.acmicpc.net/problem/6549

참고 : https://blog.encrypted.gg/135


스택

너무 안풀려서 발킹도그님의 블로그를 참고했습니다.

 

스택을 이용하면 O(N) 만에 앞에서부터 순회하며 문제를 해결할 수 있습니다.

● 현재 스택의 top 보다 높이가 큰 Histogram 은 높이와 인덱스를 스택에 추가해줍니다.

( 따라서, 스택에는 높이가 증가하는 순서대로 쌓여있습니다. )

● 현재 스택의 top보다 높이가 작거나 같은 Histogram 이 들어왔을때는 다음과 같은 과정을 반복합니다.

1) 현재 top의 값보다 Histogram 의 높이가 커질 때까지 답을 갱신하며 pop을 진행한다. 이때, 스택에는 높이가 커지는 순으로 값이 쌓여있으므로 현재 높이 * 스택에서 꺼낸 블록의 수 로 최대 넓이를 구할 수 있습니다.

ex)  3 - 4 - 5 가 있으면 우리는 높이 4를 선택할거면 높이 5까지 같이 선택해서 4 * 2 의 넓이를 얻는게 최대값이고, 높이 3도 마찬가지로 높이 3을 선택할거면 4, 5 도 같이 선택해서 3 * 3 의 넓이를 얻는게 최대값이 됩니다.

2) 1)의 과정을 진행하며 현재 Histogram 보다 크거나 같은 마지막 Histogram 의 index를 지금 높이와 같이 스택에 Push 해줍니다. 이 과정이 가장 중요한데, 예를 들어 3 - 4 - 5 - 2 가 있다면 우리는 2를 만났을 때, 현재 스택의 top 보다 높이가 작거나 같은 Histogram 이 들어왔으므로 1)의 과정을 수행해야합니다. 따라서, 5 * 1, 4 * 2, 3 * 3 으로 넓이를 구해가며 최대 넓이를 갱신하고, 이제 높이가 2인 Histogram 을 스택에 Push해줘야하는데 생각해보면 3 - 4 - 5 는 높이 2보다 다 크거나 같으므로 높이 2를 포함해서 최대 넓이를 구하려면 3 - 4 - 5 를 무조건 포함해서 히스토그램의 갯수를 늘려줘야 합니다. 따라서, 높이가 2인 Histogram을 3 - 4 - 5 를 포함한 index로 스택에 Push 해줍니다.

#include<iostream>
#include<stack>
#include<algorithm>

using namespace std;

int main(void) {
	ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);

	int N, height[100000] = {};
	while (true) {
		cin >> N;
		if (N == 0)
			break;
		for (int i = 0; i < N; i++)
			cin >> height[i];

		long long ans = 0;
		stack<pair<int, int>> s;
		for (int i = 0; i < N; i++) {
			int point = i;
			while (!s.empty() && s.top().first >= height[i]) { 
				ans = max(ans, 1LL * (i - s.top().second) * s.top().first); 
				point = s.top().second;
				s.pop();
			}
			s.push({ height[i], point });
		}
		while (!s.empty()) {
			ans = max(ans, 1LL * (N - s.top().second) * s.top().first);
			s.pop();
		}
		cout << ans << '\n';
	}
	return 0;
}

 

문제 : https://www.acmicpc.net/problem/17178


스택 정렬 투포인터

● 대기공간은 마지막에 들어온 사람이 가장 먼저 나갈 수 있으므로, 자료구조 스택을 이용하면 쉽게 구현할 수 있습니다.

● 들어온 입력을 list에 담고, list를 정렬한 배열을 order 라고 하자. 투 포인터를 이용해 list와 order를 동시에 순회하면 된다.

1) list[i] 와 order[j] 가 같다면 입장이 가능한 경우이므로 i와 j를 다음 칸으로 옮겨줍니다.

2) list[i]와 order[j]는 다르지만 stack(대기공간)의 top과 order[j]가 같다면 stack을 pop하고 j 를 다음 칸으로 옮겨줍니다.

3) 그것도 아니라면 대기 공간으로 list[i]를 보내버리고 i를 다음 칸으로 옮겨줍니다.

● 순회가 다 끝나고 아직 stack(대기공간)이 비어있지 않다면 입장이 완료된 것이 아니므로 order 과 비교해가며 한 명씩 입장을 시킵니다. 이때, order[j]와 stack의 top이 다르다면 순서대로 입장한 것이 아니므로 BAD를 출력합니다.

#include<iostream>
#include<string>
#include<stack>
#include<algorithm>
#include<vector>

using namespace std;

bool cmp(string a, string b) {
	if (a[0] < b[0])
		return true;
	else if (a[0] > b[0])
		return false;
	else{
		string temp1 = {}, temp2 = {};
		for (int l = 2; l < a.size(); l++)
			temp1 += a[l];
		for (int l = 2; l < b.size(); l++)
			temp2 += b[l];
		int ntemp1 = stoi(temp1), ntemp2 = stoi(temp2);
		if (ntemp1 < ntemp2)
			return true;
		return false;
	}
}

int main(void) {
	ios::sync_with_stdio(false);
	cin.tie(0);

	int N;
	string x;
	vector<string> list;
	vector<string> order;

	cin >> N;
	for (int i = 0; i < N; i++)
		for (int j = 0; j < 5; j++) {
			cin >> x;
			list.push_back(x);
			order.push_back(x);
		}

	sort(order.begin(), order.end(), cmp);
	
	bool flag = false;
	int i = 0, j = 0;
	stack<string> s;

	while (i <  5 *N && j < 5 * N) {
		if (list[i] == order[j]){
			i++, j++;
		}
		else if (!s.empty() && s.top() == order[j]){
			s.pop();
			j++;
		}
		else {
			s.push(list[i]);
			i++;
		}
	}
	while (!s.empty()) {
		if (s.top() != order[j]) {
			flag = true;
			break;
		}
		s.pop(), j++;
	}

	if (flag)
		cout << "BAD\n";
	else
		cout << "GOOD\n";
	return 0;
}

 

+ Recent posts