반응형

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

 

2573번: 빙산

첫 줄에는 이차원 배열의 행의 개수와 열의 개수를 나타내는 두 정수 N과 M이 한 개의 빈칸을 사이에 두고 주어진다. N과 M은 3 이상 300 이하이다. 그 다음 N개의 줄에는 각 줄마다 배열의 각 행을

www.acmicpc.net

접근방법[알고리즘]

1. 빙산이 몇개의 덩어리로 이루어져있는지 BFS를 이용해 개수를 센다.

2. 빙산의 개수가 2개이상이거나 0이 아니면 빙산을 한차례 녹인다.

3. 1과 2를 반복한다.

 


주의할 점

빙산이 녹아 없어지는 경우 바로 0으로 만들어버리면
다음 빙산에 영향을 줘서 틀린다.
빙산이 0이 되는 경우는 따로 저장해뒀다가 큐를 한바퀴 다 돈다음
적용 해주어야 한다.

 


문제풀이코드 C++

#include <bits/stdc++.h>
using namespace std;

int n, m, arr[301][301] ,t;
int dx[4] = { 1,0,-1,0 };
int dy[4] = { 0,1,0,-1 };

int Count() {
	int check[301][301];
	int ans = 1;
	memset(check, 0, sizeof(check));
	queue<pair<int, int> > Q;

	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++) {
			if (arr[i][j] > 0 && check[i][j]==0) {
				check[i][j] = ans;
				Q.push({ i,j });

				while (!Q.empty()) {
					int x = Q.front().first;
					int y = Q.front().second;
					Q.pop();

					for (int k = 0; k < 4; k++) {
						int nx = x + dx[k];
						int ny = y + dy[k];
						if (nx < 0 || nx >= n || ny < 0 || ny >= m || arr[nx][ny] == 0) continue;
						if (check[nx][ny] == 0) {
							check[nx][ny] = ans;
							Q.push({ nx,ny });
						}
					}
				}
				ans++;
			}
		}
	}
	return ans - 1;
}


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

	queue<pair<int, int> > Q;
	cin >> n >> m;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++) {
			cin >> arr[i][j];
			if (arr[i][j] > 0) Q.push({ i,j });
		}
	}


	while (1) {
		int area = Count();
		if (area >= 2) {
			cout << t << '\n';
			return 0;
		}
		else if (area == 0) {
			cout << 0 << '\n';
			return 0;
		}

		int s = Q.size();
		//빙산이 녹아 없어지는 경우 바로 0으로 만들어버리면
		//다음 빙산에 영향을 줘서 틀린다.
		//빙산이 0이 되는 경우는 따로 저장해뒀다가 큐를 한바퀴 다 돈다음
		//적용 해주어야 한다.

		vector<pair<int, int> > melt;
		for (int i = 0; i < s; i++) {
			int x = Q.front().first;
			int y = Q.front().second;
			Q.pop();

			int cnt = 0;
			for (int j = 0; j < 4; j++) {
				int nx = x + dx[j];
				int ny = y + dy[j];
				if (nx < 0 || nx >= n || ny < 0 || ny >= m || arr[nx][ny]>0) continue;
				cnt++;
			}

			if (cnt >= arr[x][y]) {
				melt.push_back({ x,y });
			}
			else {
				arr[x][y] -= cnt;
				Q.push({ x,y });
			}
		}
		for (int i = 0; i < melt.size(); i++) {
			arr[melt[i].first][melt[i].second] = 0;
		}

		t++;
	}

	return 0;
}

백준 2573번

 

반응형
반응형

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

 

15971번: 두 로봇

2018년 강원도에서 새로운 동굴이 발견되었다. 이 동굴에는 총 N개의 넓은 방이 존재하며 좁은 통로로 서로 연결되어 있는 것으로 밝혀졌다. N개의 방은 1번부터 N번까지의 번호를 붙여 1번 방, 2번

www.acmicpc.net

 

1.문제설명

 

백준 15971번 : 두 로봇

두 로봇이 통신하기 위해 이동해야하는 최단 거리를 구해야한다.

두 로봇은 같은 노드에 있거나 서로 연결된 노드에 위치하면 통신할 수 있다.

 

 

 

 

2.접근방법[알고리즘]

처음에는 문제에 주어진 대로 로봇 두대를 놓고 한 로봇 씩 이동시키면서 

통신할 수 있는지 확인하고 그 때 이동한 거리를 계속 갱신해주면서 답을 구했는데

이러면 n이 10만이기 때문에 시간초과로 틀렸다.

 

 

문제의 조건에서

N개의 노드와 N-1의 간선이 있기 때문에

이미 지나온 노드를 재방문 할 수 없을 때

출발 지점에서 도착 지점까지 가는 경로는 무조건 하나만 나오고, 그게 최단 경로가 된다.

 

이 문제를 해결하려면

1. 두 로봇의 시작점 사이의 거리를 구한다.

2. 시작점을 이어주는 통로 중 길이가 가장 긴 간선을 구한다.

3. 답 = 시작점 사이 거리 - 가장 긴 간선 거리 

 

 

 


 

 

 

 

3.문제풀이코드 C++

#include <bits/stdc++.h>
using namespace std;

int n, s1, s2;
vector<pair<int, int> > m[100001];

bool ch[100001];
bool flag = false;

void DFS(int x, int sum, int max_dist) {
	if (flag) return;
	if (x == s2) {
		cout << sum - max_dist << '\n';
		flag = true;
		return;
	}

	for (int i = 0; i < m[x].size(); i++) {
		int nx = m[x][i].first;
		int nd = m[x][i].second;
		if (ch[nx] == 0) {
			ch[nx] = 1;
			DFS(nx, sum + nd, max(nd,max_dist));
		}
	}

	return;
}

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

	cin >> n >> s1 >> s2;
	for (int i = 0; i < n - 1; i++) {
		int a, b, c;
		cin >> a >> b >> c;
		m[a].push_back({ b,c });
		m[b].push_back({ a,c });
	}
	ch[s1] = 1;
	DFS(s1, 0, 0);

	return 0;
}

백준 15971번 두 로봇

DFS를 돌면서 다른 로봇의 시작점을 만나면 바로 종료해주면 된다.

 

해당 정점까지 가는 다른 방법이 존재하지 않기 때문이다.

 

 

 

반응형
반응형

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

 

2636번: 치즈

아래 <그림 1>과 같이 정사각형 칸들로 이루어진 사각형 모양의 판이 있고, 그 위에 얇은 치즈(회색으로 표시된 부분)가 놓여 있다. 판의 가장자리(<그림 1>에서 네모 칸에 X친 부분)에는 치즈가 놓

www.acmicpc.net

#include <bits/stdc++.h>
using namespace std;

int n, m, arr[103][103], ans;
queue<pair<int, int> > Q;
int dx[4] = { 1,0,-1,0 };
int dy[4] = { 0,1,0,-1 };


// 1. 공기 구역 BFS
// 2. 공기와 접한 치즈 사라짐
// 1, 2 반복 
bool air[103][103];
queue<pair<int, int> > airQ;


void air_spread() {
	while (!airQ.empty()) {
		int x = airQ.front().first;
		int y = airQ.front().second;
		airQ.pop();

		for (int i = 0; i < 4; i++) {
			int nx = x + dx[i];
			int ny = y + dy[i];

			if (nx < 0 || nx >= n || ny < 0 || ny >= m || air[nx][ny] == 1) continue;
			if (arr[nx][ny] == 0) {
				airQ.push({ nx,ny });
				air[nx][ny] = 1;
			}
		}
	}
}


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

	cin >> n >> m;
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < m; j++) {
			cin >> arr[i][j];
			if (arr[i][j] == 1) {
				Q.push({ i,j });
			}
		}
	}

	// 맨 처음 공기인 부분 
	for (int i = 0; i < n; i++) {
		air[i][0] = 1;
		airQ.push({ i,0 });
		air[i][m - 1] = 1;
		airQ.push({ i,m - 1 });
	}

	for (int i = 0; i < m; i++) {
		air[0][i] = 1;
		airQ.push({ 0,i });
		air[n-1][i] = 1;
		airQ.push({ n-1, i});
	}


	int t = Q.size();
	while (!Q.empty()) {
		air_spread();
		t = Q.size();
		vector<pair<int, int> > melt;

		for (int q = 0; q < t; q++) {
			int x = Q.front().first;
			int y = Q.front().second;
			Q.pop();

			bool has_hole = false;
			for (int i = 0; i < 4; i++) {
				int nx = x + dx[i];
				int ny = y + dy[i];

				if (nx < 0 || nx >= n || ny < 0 || ny >= m) continue;
				if (air[nx][ny] == 1) {
					melt.push_back({ x,y });
					has_hole = true;
					break;
				}
			}
			if (!has_hole) Q.push({ x,y });
		}

		for (int i = 0; i < melt.size(); i++) {
			arr[melt[i].first][melt[i].second] = 0;
			air[melt[i].first][melt[i].second] = 1;
			airQ.push({ melt[i].first, melt[i].second });
		}
		ans++;
	}

	cout << ans << '\n';
	cout << t << '\n';
	return 0;
}

백준 2636번 

 

반응형
반응형

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

 

1753번: 최단경로

첫째 줄에 정점의 개수 V와 간선의 개수 E가 주어진다. (1 ≤ V ≤ 20,000, 1 ≤ E ≤ 300,000) 모든 정점에는 1부터 V까지 번호가 매겨져 있다고 가정한다. 둘째 줄에는 시작 정점의 번호 K(1 ≤ K ≤ V)가

www.acmicpc.net

 

#include <bits/stdc++.h>
using namespace std;

int ans[20001], v, e, st;

struct Node {
	int n, d;

	bool operator<(const Node& b) const {
		return d > b.d;
	}
};

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

	fill(ans, ans + 20001, -1);

	cin >> v >> e;
	cin >> st;

	vector<pair<int, int> > m[20001];

	for (int i = 0; i < e; i++) {
		int a, b, c;
		cin >> a >> b >> c;
		m[a].push_back({ b,c });
	}

	priority_queue<Node> Q;
	Q.push({ st,0 });

	while (!Q.empty()) {
		int cur_node = Q.top().n;
		int cur_dist = Q.top().d;
		Q.pop();

		if (ans[cur_node] == -1) {
			ans[cur_node] = cur_dist;

			for (int i = 0; i < m[cur_node].size(); i++) {
				int nx = m[cur_node][i].first;
				if (ans[nx] == -1) {
					int nd = m[cur_node][i].second + cur_dist;
					Q.push({ nx,nd });
				}
			}
		}
	}


	for (int i = 1; i <= v; i++) {
		if (ans[i] == -1) cout << "INF\n";
		else cout << ans[i] << '\n';
	}


	return 0;
}

우선순위큐 최소힙을 이용해

다익스트라 알고리즘으로 풀 수 있다.

1753번 최단경로 메모리 및 시간

 

반응형
반응형

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

 

3111번: 검열

첫째 줄에 단어 A가, 둘째 줄에 텍스트 T가 주어진다. A와 T는 알파벳 소문자로만 이루어져 있고, A는 최대 25자, T는 최대 300,000자이다.

www.acmicpc.net

 

#include <bits/stdc++.h>
using namespace std;

string a, t;

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

	cin >> a >> t;

	int l = 0, r = t.length() - 1, turn = 0;
	deque<char> left, right;

	while (l <= r) {
		if (turn % 2 == 0) {
			left.push_back(t[l++]);

			if (left.size() >= a.length()) {
				bool is_same = true;
				for (int i = 0; i < a.length(); i++) {
					if (a[a.length() - 1 - i] != left[left.size() - 1 - i]) {
						is_same = false;
						break;
					}
				}
				if (is_same) {
					for (int i = 0; i < a.length(); i++)
						left.pop_back();

					turn++;
				}
			}
			
		}
		else {

			right.push_front(t[r--]);
			if (right.size() >= a.length()) {

				bool is_same = true;
				for (int i = 0; i < a.length(); i++) {
					if (a[i] != right[i]) {
						is_same = false;
						break;
					}
				}
				if (is_same) {
					for (int i = 0; i < a.length(); i++)
						right.pop_front();

					turn++;
				}
			}
		}

	}

	while (!right.empty()) {
		left.push_back(right.front());
		right.pop_front();

		if (left.size() >= a.length() && left.back() == a[a.length() - 1]) {
			bool is_same = true;
			for (int i = 0; i < a.length(); i++) {
				if (a[a.length() - 1 - i] != left[left.size() - 1 - i]) {
					is_same = false;
					break;
				}
			}
			if (is_same) {
				for (int i = 0; i < a.length(); i++)
					left.pop_back();
			}
		}
	}

	while (!left.empty()) {
		cout << left.front();
		left.pop_front();
	}
	cout << '\n';

	return 0;
}

백준 3111

 

반응형
반응형

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

 

2042번: 구간 합 구하기

첫째 줄에 수의 개수 N(1 ≤ N ≤ 1,000,000)과 M(1 ≤ M ≤ 10,000), K(1 ≤ K ≤ 10,000) 가 주어진다. M은 수의 변경이 일어나는 횟수이고, K는 구간의 합을 구하는 횟수이다. 그리고 둘째 줄부터 N+1번째 줄

www.acmicpc.net


Segment Tree 공부하기

 

겉보기에는 구간합을 구하는 간단한 문제처럼보이지만

Segment Tree 자료구조를 이해해야 풀 수 있는 문제입니다.

하단 링크에 설명이 잘 되어 있습니다.

 

https://www.acmicpc.net/blog/view/9

 

세그먼트 트리 (Segment Tree)

문제 배열 A가 있고, 여기서 다음과 같은 두 연산을 수행해야하는 문제를 생각해봅시다. 구간 l, r (l ≤ r)이 주어졌을 때, A[l] + A[l+1] + ... + A[r-1] + A[r]을 구해서 출력하기 i번째 수를 v로 바꾸기. A[i

www.acmicpc.net

 

하단의 영상은 제가 올린 건 아니고

유튜브에 개발자영맨님이 만든 Segment Tree 강의인데

설명이 잘 되어 있어서 같이 올려봅니다.

https://www.youtube.com/watch?v=075fcq7oCC8 

https://www.youtube.com/watch?v=ahFB9eCnI6c 

 

 

 


 

문제풀이코드 C++

#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
using namespace std;

int n, m, k;
long long arr[1000003];

struct SegmentTree {
	int N;
	vector<long long> tree;

	long long merge(long long left, long long right) {
		return left + right;
	}

	long long buildRec(int node, int nodeLeft, int nodeRight) {
		if (nodeLeft == nodeRight) {
			return tree[node] = arr[nodeLeft];
		}

		int mid = (nodeLeft + nodeRight) / 2;

		long long LeftVal = buildRec(2 * node, nodeLeft, mid);
		long long RightVal = buildRec(2 * node + 1, mid + 1, nodeRight);

		return tree[node] = merge(LeftVal, RightVal);
	}

	void init(int Size) {
		N = Size;
		tree.resize(N * 4);
		buildRec(1, 0, N - 1);
	}

	long long queryRec(int left, int right, int node, int nodeLeft, int nodeRight) {
		// 노드 구간이 구하는 구간 밖일 경우 0 리턴
		if (nodeRight < left || right < nodeLeft) {
			return 0;
		}

		if (left <= nodeLeft && nodeRight <= right) {
			return tree[node];
		}

		int mid = (nodeLeft + nodeRight) / 2;
		long long leftVal = queryRec(left, right, 2 * node, nodeLeft, mid);
		long long rightVal = queryRec(left, right, 2 * node + 1, mid + 1, nodeRight);

		return merge(leftVal, rightVal);
	}


	long long query(int left, int right) {
		return queryRec(left, right, 1, 0, N - 1);
	}

	long long updateRec(int index, long long newValue, int node, int nodeLeft, int nodeRight) {
		if (nodeRight < index || index < nodeLeft) return tree[node];
		
		if (nodeLeft == nodeRight) {
			return tree[node] = newValue;
		}

		int mid = (nodeLeft + nodeRight) / 2;
		long long leftVal = updateRec(index, newValue, 2 * node, nodeLeft, mid);
		long long rightVal = updateRec(index, newValue, 2 * node + 1, mid + 1, nodeRight);


		return tree[node] = merge(leftVal, rightVal);
	}


	void update(int index, long long newValue) {
		updateRec(index, newValue, 1, 0, N - 1);
	}

};


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

	cin >> n >> m >> k;

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

	SegmentTree st;
	st.init(n);

	for (int i = n + 2; i <= n + m + k + 1; i++) {
		int a, b;
		long long c;
		cin >> a >> b >> c;

		if (a == 2) {
			cout << st.query(b - 1, c - 1) << '\n';
		}
		else {
			st.update(b - 1, c);
		}
	}

	
	return 0;
}

백준 2042번 구간 합 구하기

 


 

백준 2042 반례

5 1 1
1
2
3
4
5
1 2 300000000000
2 2 5
answer: 300000000012

a, b, c 를 입력 받을 때 c를 long long 형으로 입력받아야합니다.

 

업데이트를 해주는 과정에서도 long long 자료형을 유지해 업데이트를 진행해야합니다.

 

 

 

반응형
반응형
struct SegmentTree {
	int N;
	vector<int> tree;

	int merge(int left, int right) {
		return left + right;
	}

	int buildRec(const int arr[], int node, int nodeLeft, int nodeRight) {
		if (nodeLeft == nodeRight)
			return tree[node] = arr[nodeLeft];

		int mid = (nodeLeft + nodeRight) / 2;
		int leftVal = buildRec(arr, node * 2, nodeLeft, mid);
		int rightVal = buildRec(arr, node * 2 + 1, mid+1, nodeRight);

		return tree[node] = merge(leftVal, rightVal);
	}

	void build(const int arr[], int Size) {
		N = Size;
		tree.resize(N*4);
		buildRec(arr, 1, 0, N - 1);
	}


	int queryRec(int left, int right, int node, int nodeLeft, int nodeRight) {
		if (nodeRight < left || right < nodeLeft) return 0;

		if (left <= nodeLeft && nodeRight <= right) return tree[node];

		int mid = (nodeLeft + nodeRight) / 2;
		int leftVal = queryRec(left, right, node * 2, nodeLeft, mid);
		int rightVal = queryRec(left, right, node * 2 + 1, mid + 1, nodeRight);

		return merge(leftVal, rightVal);

	}

	int query(int left, int right) {
		return queryRec(left, right, 1, 0, N - 1);
	}

	// 하나만 업데이트
	int updateRec(int index, int newValue, int node, int nodeLeft, int nodeRight) {
		if (index < nodeLeft || nodeRight < index) {
			return tree[node];
		}
		if (nodeLeft == nodeRight) {
			return tree[node] = newValue;
		}


		int mid = (nodeLeft + nodeRight) / 2;
		int updateLeft = updateRec(index, newValue, node*2, nodeLeft, mid);
		int updateRight = updateRec(index, newValue, node*2+1, mid + 1, nodeRight);

		return tree[node] = merge(updateLeft, updateRight);
	}


	int update(int index, int newValue) {
		return updateRec(index, newValue, 1, 0, N - 1);
	}

	// 여러개 업데이트 
	int updateRec(int left, int right, int newValue, int node, int nodeLeft, int nodeRight) {
		if (nodeRight < left || right < nodeLeft) {
			return tree[node];
		}
		if (nodeLeft == nodeRight) {
			return tree[node] = newValue;
		}


		int mid = (nodeLeft + nodeRight) / 2;
		int updateLeft = updateRec(left,right, newValue, node * 2, nodeLeft, mid);
		int updateRight = updateRec(left,right, newValue, node * 2 + 1, mid + 1, nodeRight);

		return tree[node] = merge(updateLeft, updateRight);
	}


	int update(int left, int right, int newValue) {
		return updateRec(left , right, newValue, 1, 0, N - 1);
	}
};
반응형
반응형

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

 

9935번: 문자열 폭발

첫째 줄에 문자열이 주어진다. 문자열의 길이는 1보다 크거나 같고, 1,000,000보다 작거나 같다. 둘째 줄에 폭발 문자열이 주어진다. 길이는 1보다 크거나 같고, 36보다 작거나 같다. 두 문자열은 모

www.acmicpc.net

1.문제설명

 

INPUT

12ab112ab2ab
12ab

첫 째로 일반 문자열이 주어지고

두번째로 폭발하는 문자열이 주어진다.

1. 일반 문자열 내에 폭발 문자열이 있을 경우 폭발 문자열을 없애줘야한다.

2. 폭발 문자열을 없앤 결과 또 폭발문자열이 포함되어 있을 경우 다시 폭발문자열을 없애줘야한다.

 

1과 2를 문자열 내에 폭발문자열이 없을 때까지 반복한다.

 

 

 


2.접근방법[알고리즘]

백준 문자열 폭발

그동안 습관적으로 STL을 사용하던 버릇에 긴장감을 주는 문제다.

 

스택을 직접 구현해야 가장 편하게 풀 수 있다.

 

입력받은 첫째 문자열을 돌면서 stack에 char 문자 하나씩 넣어주다가

stack의 가장 위 문자가 폭발 문자열의 마지막 문자와 동일하면

그 문자가 폭발 문자열을 이루고 있는지 확인하고,

 

폭발문자열이 발견되면 그만큼 pop 해주어야한다.

pop 해주는 건 p 변수를 폭발문자열의 크기 만큼 빼주면 쉽게 구현할 수 있다.

 

 


3.문제풀이코드 C++

#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
using namespace std;

char stack[1000001];
int p = 0;
string str;
string bomb;

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

	cin >> str;
	cin >> bomb;
	
	for (int i = 0; i < str.length(); i++) {
		stack[p++] = str[i];

		if (p >= bomb.length() && stack[p - 1] == bomb[bomb.length() - 1]) {
			bool is_same = true;
			for (int j = 0; j < bomb.length(); j++) {
				if (bomb[j] != stack[p - bomb.length() + j]) {
					is_same = false;
				}
			}
			if (is_same) {
				p -= bomb.length();
			}
		}
	}

	if (p == 0) {
		cout << "FRULA" << '\n';
	}
	else {
		for (int i = 0; i < p; i++) {
			cout << stack[i];
		}
		cout << '\n';
	}


	return 0;
}

백준 9935번 : 문자열 폭발

반응형

+ Recent posts