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

 

2261번: 가장 가까운 두 점

첫째 줄에 자연수 n(2 ≤ n ≤ 100,000)이 주어진다. 다음 n개의 줄에는 차례로 각 점의 x, y좌표가 주어진다. 각각의 좌표는 절댓값이 10,000을 넘지 않는 정수이다. 같은 점이 여러 번 주어질 수도 있다.

www.acmicpc.net


문제해설

이 문제는 "라인스위핑"알고리즘으로 풀어 보았습니다.

이문제의 정답률은 15% 미만이고, 인터넷상에 해답이나 설명이 없었다면 10%도 안나올거라고 생각됩니다.

 

입력 조건에서, 좌표의 갯수가 10만개 까지 들어오기 때문에

당연히 O(n^2) 풀이로는 TLE이 나올 것입니다.

적어도 O(NlgN)정도의 풀이가 필요 합니다.

 

우선 이문제의 핵심은, 답을 만들 수 있는 최소한의 후보들로만 비교하는 것입니다.

 편의를 위해, 초기에 기준을 잡고 기준을 기준으로

비교하면서 기준을 갱신해 나가며 답을 찾으면 됩니다.

( 주석으로 상세한 과정을 풀이했습니다 )

 

< 정렬 및 기준 잡기 >

	// x축 정렬을 하는 이유 ?
	// x축 정렬을 통해 x의축의 차이만으로 최대값 후보에서 여러 점들을 제외 시킬 수 있다.
	sort(vec.begin(), vec.end());
	// {y,x}으로 insert한 이유?
	// dx를 통해 if문으로 x축과의 거리는 처리 하였고,
	// map에 남아잇는 점들중에서 y축으로 lower_bound, upper_bound를 하기 위함이다.
	// = 0 인이 이유?
	// 어떤 값이든 크게 상관없지만 , INF보단 작고, -INF보단 커야한다.( lower_bound, upper_bound를 위해 )
	m[{vec[0].second, vec[0].first}] = 0; 
	m[{vec[1].second, vec[1].first}] = 0;
	ll ans = getDist(vec[0], vec[1]);

< 후보를 추가, 제외하면서 탐색 >

	int pos = 0;
	for (int i = 2; i < n; i++) {
		// pos부터 i-1번까지 i번째점과 비교를 한다.
		while (pos < i) {
			// x축간의 거리 차이
			int dx = vec[i].first - vec[pos].first;

			// pos( i 점 기준으로 현재 map 있는 가장 왼쪽 점 ) 과 i번째 점간의 x축 거리가
			// 더 작다면 pos점은 여전히 후보에 포함될 수 있다.
			if (dx * dx <= ans) break;
			
			// pos(i 점 기준으로 현재 map 있는 가장 왼쪽 점) 과 i번째 점간의 x축 거리가
			// 더 크다면 pos점은 더이상 더 작은 ans값을 만들어내지 못한다.( 후보에서 제외된다 )
			m.erase({ vec[pos].second,vec[pos].first });
			pos++;
		}

		// getDist 는 sqrt를 없이 반환했음으로...
		ll dis = sqrt(ans);

		// map에 남아있는 점들중에서
		// 현재 ans보다 작아 질 수 있는 후보 점들을 확인 하기위해 left, right를 찾고
		auto left = m.lower_bound({ vec[i].second - dis, -INF });
		auto right = m.upper_bound({ vec[i].second + dis, INF });

		// ans 갱신
		for (auto l = left; l != right; l++) {
			ans = min(ans, getDist( {l->first.second,l->first.first}, vec[i]));
		}

		// i번째점은 당연히 뒤에 확인할게 있음으로 후보 점이 된다.
		m[{vec[i].second, vec[i].first}] = 0;
	}

 

 

그림으로 보나, 코드로 보나 쉽게 이해가 가지 않을 수 있을 것 같습니다.

때문에 중간 중간 주석으로, 과정에대해서 나름 상세하게 설명해 보았습니다.

 


전체코드

#include <iostream>
#include <algorithm>
#include <cmath>
#include <map>
#include <vector>
#include <limits.h>
using namespace std;

#define INF INT_MAX
typedef long long ll;

// 문제에서 요구하는 거리는 거리의 제곱이기 때문에 sqrt를 사용하지 않았습니다.
ll getDist(pair<int,int> a, pair<int,int> b)
{
	int x1 = a.first;
	int y1 = a.second;
	int x2 = b.first;
	int y2 = b.second;
	return (x2-x1)*(ll)(x2 - x1) + (y2-y1)*(ll)(y2-y1);
}

vector<pair<int, int>> vec;
map<pair<int, int>, int> m;

int main()
{
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int n; cin >> n;
	for (int i = 0; i < n; i++)
	{
		int x, y;
		cin >> x >> y;
		vec.push_back({ x,y });
	}
	// x축 정렬을 하는 이유 ?
	// x축 정렬을 통해 x의축의 차이만으로 최대값 후보에서 여러 점들을 제외 시킬 수 있다.
	sort(vec.begin(), vec.end());
	// {y,x}으로 insert한 이유?
	// dx를 통해 if문으로 x축과의 거리는 처리 하였고,
	// map에 남아잇는 점들중에서 y축으로 lower_bound, upper_bound를 하기 위함이다.
	// = 0 인이 이유?
	// 어떤 값이든 크게 상관없지만 , INF보단 작고, -INF보단 커야한다.( lower_bound, upper_bound를 위해 )
	m[{vec[0].second, vec[0].first}] = 0; 
	m[{vec[1].second, vec[1].first}] = 0;
	ll ans = getDist(vec[0], vec[1]);

	// pos = 0 부터, i = 2 부터인 이유?
	// 편의상 첫번째점과 두번째점을 (x축 정렬 기준 ) 을 기준으로 잡았다.
	// 밑의 코드의 for문을 돌리는대신 기준을 잡았다고 보면 된다.
	/*
	for(int i = 0 ; i < 2; i ++){
		...
	}
	*/
	int pos = 0;
	for (int i = 2; i < n; i++) {
		// pos부터 i-1번까지 i번째점과 비교를 한다.
		while (pos < i) {
			// x축간의 거리 차이
			int dx = vec[i].first - vec[pos].first;

			// pos( i 점 기준으로 현재 map 있는 가장 왼쪽 점 ) 과 i번째 점간의 x축 거리가
			// 더 작다면 pos점은 여전히 후보에 포함될 수 있다.
			if (dx * dx <= ans) break;
			
			// pos(i 점 기준으로 현재 map 있는 가장 왼쪽 점) 과 i번째 점간의 x축 거리가
			// 더 크다면 pos점은 더이상 더 작은 ans값을 만들어내지 못한다.( 후보에서 제외된다 )
			m.erase({ vec[pos].second,vec[pos].first });
			pos++;
		}

		// getDist 는 sqrt를 없이 반환했음으로...
		ll dis = sqrt(ans);

		// map에 남아있는 점들중에서
		// 현재 ans보다 작아 질 수 있는 후보 점들을 확인 하기위해 left, right를 찾고
		auto left = m.lower_bound({ vec[i].second - dis, -INF });
		auto right = m.upper_bound({ vec[i].second + dis, INF });

		// ans 갱신
		for (auto l = left; l != right; l++) {
			ans = min(ans, getDist( {l->first.second,l->first.first}, vec[i]));
		}

		// i번째점은 당연히 뒤에 확인할게 있음으로 후보 점이 된다.
		m[{vec[i].second, vec[i].first}] = 0;
	}

	cout << ans << "\n";
	
	return 0;
}

 


 

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

 

1298번: 노트북의 주인을 찾아서

어느 날 모든 학생들은 한 명이 한개의 노트북을 가지고 공부하던 도중, 자리를 바꾸다가 그만 노트북이 뒤섞이고 말았다. 대다수의 학생들은 자신의 노트북을 잘 알고 있어서 자신의 노트북을 받을 수 있었지만, 애석하게도 N명의 학생들이 정확히 자신의 노트북이 어떤것인지 알지 못했다. 왜냐하면 그들은 노트북을 구입한게 바로 어제였기 때문이다. 어차피 새것인 노트북, 바뀐들 어떠하랴. 그들에게 자신의 노트북이라고 생각되는 노트북들을 얘기해 보라고 했다. 이번에는

www.acmicpc.net


문제해설

각 시작 노드 ( 사람 ) 에서

노트북 주인이 없다면 flow를 발생시켜서

노트북을 갖게 하면 됩니다.


전체코드

#include <iostream>
#include <vector>
#include <stack>
#include <queue>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define endl '\n'
//--------------------------------------------------------------------------------------

#define MAX_N 101

vector<int> map[MAX_N];
int occupy[MAX_N];
int occupied[MAX_N];
bool visit[MAX_N];
bool dfs(int x) {
	visit[x] = true;
	for (int i = 0; i < map[x].size(); i++) {
		int y = map[x][i];
		if (occupied[y] == -1 || (!visit[occupied[y]] && dfs(occupied[y]))) {
			occupy[x] = y;
			occupied[y] = x;
			return true;
		}
	}
	return false;
}

int main() {
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int N, M; cin >> N >> M;
	for (int i = 0; i < M; i++) {
		int a, b; cin >> a >> b;
		map[a].push_back(b);
	}
	memset(occupy, -1, sizeof(occupy));
	memset(occupied, -1, sizeof(occupied));
	int ans = 0;
	for (int i = 1; i <= N; i++) {
		if (occupy[i] == -1) {
			memset(visit, false, sizeof(visit));
			if (dfs(i)) ans++;
		}
	}
	cout << ans << endl;
}

기타

'알고리즘 > [C++]BOJ' 카테고리의 다른 글

[백준BOJ] 2261 가장 가까운 두 점  (1) 2019.10.19
[백준BOJ] 11377 열혈강호 3  (0) 2019.10.14
[백준BOJ] 1671 상어의 저녁식사  (0) 2019.10.14

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

 

11377번: 열혈강호 3

첫째 줄에 직원의 수 N과 일의 개수 M, 일을 2개할 수 있는 직원의 수 K가 주어진다. (1 ≤ N, M ≤ 1,000, 1 ≤ K ≤ N) 둘째 줄부터 N개의 줄의 i번째 줄에는 i번 직원이 할 수 있는 일의 개수와 할 수 있는 일의 번호가 주어진다.

www.acmicpc.net


문제해설

열혈강호3  = 열혈강호1 + 열혈강호2

라고 보시면 되는 문제입니다. 

  • 우선 모든 사람에게 한가지 일을 시킵니다.
  • 한가지 일을 한 사람들 중에서 한가지일을 더 시킵니다. 

전체코드

#include <iostream>
#include <vector>
#include <stack>
#include <queue>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
using namespace std;
typedef long long ll;\
typedef pair<int, int> pii;
#define endl '\n'
#define cout1(a) {cout << a << endl;}
#define cout2(a,b) {cout << a << " " < b << endl;}
//--------------------------------------------------------------------------------------

#define MAX_N 1001
vector<int> map[MAX_N];
int work[MAX_N];
int worked[MAX_N];
bool visit[MAX_N];
int workCnt[MAX_N];
bool dfs(int x) {
	visit[x] = true;
	for (int i = 0; i < map[x].size(); i++) {
		int y = map[x][i];
		if (worked[y] == -1 || (visit[worked[y]] == false && dfs(worked[y]))) {
			work[x] = y;
			worked[y] = x;
			return true;
		}
	}
	return false;
}



int main() {
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int N, M, K; cin >> N >> M >> K;
	for (int i = 1; i <= N; i++) {
		int C; cin >> C;
		for (int j = 0; j < C; j++) {
			int data; cin >> data;
			map[i].push_back(data);
		}
	}
	memset(work, -1, sizeof(work));
	memset(worked, -1, sizeof(worked));
	int ans = 0;
	int k = 0;
	for (int i = 1; i <= N; i++) {
		if (work[i] == -1) {
			memset(visit, false, sizeof(visit));
			if (dfs(i)) {
				workCnt[i]++;
				ans++;
			}
		}
	}

	for (int i = 1; i <= N; i++) {
		if (workCnt[i] == 1) {
			memset(visit, false, sizeof(visit));
			if (k < K && dfs(i)) {
				ans++;
				k++;
			}
		}
	}

	cout << ans << endl;
}

기타

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

 

1671번: 상어의 저녁식사

어떤 상어는 저녁식사로 서로를 먹는다. 모든 상어는 자신과 다른 상어의 크기, 속도, 지능을 수치로 나타낸 것을 알고 있다. 만약, 상어 A의 크기, 속도, 지능이 상어 B의 크기, 속도, 지능보다 크거나 같다면 상어 A는 상어 B를 먹을 수 있다. 그러나, 상어들의 왕 김재홍은 상어들이 많이 없어지는 것을 방지하기 위해서 한 상어가 최대 두 개의 상어만 먹을 수 있게 했다. 상어들은 김재홍의 말을 모두 듣는다. N마리 상어의 크기, 속도, 지능이 주어졌

www.acmicpc.net


문제해설

밑의 문제와 비슷한 유형의 문제입니다. 

단, 문제 조건을 잘 읽고 따져야 되는 문제입니다.

 

2019/10/13 - [알고리즘/BOJ] - [백준BOJ] 11376 열혈강호2

 

[백준BOJ] 11376 열혈강호2

https://www.acmicpc.net/problem/11376 11376번: 열혈강호 2 강호네 회사에는 직원이 N명이 있고, 해야할 일이 M개가 있다. 직원은 1번부터 N번까지 번호가 매겨져 있고, 일은 1번부터 M번까지 번호가 매겨져 있..

polohee81.tistory.com

 

우선 문제에서 제시한 조건에 의해서 내림차순으로 정렬을 해줍니다.

정렬된 vector 에서 i < j 라고 할떄

  • vec[i] 는 vec[j] 를 잡아먹을 수 있고( 단 , 속도, 지능, 크기가 모두 크거나 같야아 합니다 )
  • vec[j] 는 vec[i] 를 어떠한 경우에도 잡아 먹을 수 없습니다.

조건에 만족하는 경우 i 에서 j 에서 가능 경로가 있다고 생각하고 ( 간선의 가중치 1 )

한 상어당 2번의 flow를 발생시키면 됩니다.

 

 


전체코드

#include <iostream>
#include <vector>
#include <stack>
#include <queue>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define endl '\n'
//--------------------------------------------------------------------------------------

#define MAX_N 51
class Shark {
public:
	int size, speed, intel;
	Shark(int size, int speed, int intel) {
		this->size = size; this->speed = speed; this->intel = intel;
	}
	bool operator < (const Shark  & b) const {
		if (this->size == b.size && this->speed == b.speed) {
			return this->intel > b.intel;
		}if (this->size == b.size) {
			return this->speed > b.speed;
		}
		else {
			return this->size > b.size;
		}
	}

	bool operator >= (const Shark  & b) const {
		if (this->size >= b.size && this->speed >= b.speed && this->intel >= b.intel) {
			return true;
		}
		else {
			return false;
		}
	}
}; vector<Shark> shark;

int eat[MAX_N];
int eated[MAX_N];
bool visit[MAX_N];
int N;

bool fun(int n) {
	visit[n] = true;
	for (int i = n + 1; i < N; i++) {
		if (shark[n] >= shark[i]) {
			if (eated[i] == -1 || (!visit[eated[i]] && fun(eated[i]))) {
				eat[n] = i;
				eated[i] = n;
				return true;
			}
		}
	}
	return false;
}


int main() {
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	cin >> N;
	for (int i = 0; i < N; i++) {
		int a, b, c; cin >> a >> b >> c;
		shark.push_back(Shark(a, b, c));
	}
	sort(shark.begin(), shark.end());
	//cout << "정렬 후" << endl;
	//for (const auto &x : shark)
	//	cout << x.size << "  " << x.speed << " " << x.intel << endl;
	int ans = N;
	memset(eat, -1, sizeof(eat));
	memset(eated, -1, sizeof(eated));

	for (int i = 0; i < N; i++) {
		if (eat[i] == -1) {
			memset(visit, false, sizeof(visit));
			if (fun(i)) ans--;
			if (fun(i)) ans--;
		}
	}
	cout << ans << endl;
}

기타

'알고리즘 > [C++]BOJ' 카테고리의 다른 글

[백준BOJ] 11377 열혈강호 3  (0) 2019.10.14
[백준BOJ] 6086 최대 유량  (0) 2019.10.13
[백준BOJ] 11376 열혈강호2  (0) 2019.10.13

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

 

6086번: 최대 유량

문제 농사꾼 존은 소들이 충분한 물을 마시길 원했다. 그래서 농장에서 우물에서 외양간을 잇는 N개의 배수관의 지도를 만들기로 했다. 존은 아주 다양한 크기의 배수관들이 완전히 우연한 방법으로 연결돼있음을 알았다. 존은 파이프를 통과하는 유량을 계산하고 싶다. 두개의 배수관이 한줄로 연결 돼 있을 때 두 관의 유량 중 최솟값으로 흐르게 된다. 예를 들어 용량이 5인 파이프가 용량이 3인 파이프와 연결되면 한개의 용량 3짜리 파이프가 된다. +---5---+

www.acmicpc.net

 


문제해설

"에드몬드 카프 알고리즘(Edmonds-Karp algorithm)" 을 활용한 "네트워크 플로우" 문제입니다.

문제 제목부터가 "최대 유량"이기 때문에, 유량관련 알고리즘을 사용해야 합니다.

에드몬드 카프 알고리즘을 모르시는분은 밑의 동영상을 참고하시면 좋을 것 같습니다.

 

https://youtu.be/Wn51_ypG_T8

위, 유튜브에서 올라온 코드 그대로 작성하면 AC를 맞을 수 있습니다.

단, 조심해야 할 것은

입력 부분에서 

		map[A].push_back(B);
		map[B].push_back(A);
		c[A][B] += w;
		c[B][A] += w;

위와 같이, 양방향에 대해서 처리를 해줘야 됩니다.

주석을 통해서 자세한 풀이를 확인해주세요.


전체코드

#include <iostream>
#include <vector>
#include <stack>
#include <queue>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
#include <limits.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define endl '\n'
//--------------------------------------------------------------------------------------
#define MAX_N 52

int c[MAX_N][MAX_N]; // 흐르는 양
int f[MAX_N][MAX_N]; // 흐르고 있는 양
int visit[MAX_N]; // 한번 que가 돌때마다 visit 이 -1로 초기화 된다.
vector<int> map[MAX_N];

// 애드몬드 카프 알고리즘
int maxFlow(int start, int end) {
	int ret = 0;
	while (1) {
		memset(visit, -1, sizeof(visit)); // 모든 정점 방문 초기화
		queue<int> q;
		q.push(start); // start 점에서 end 점까지 흘려 보낸다.
		while (!q.empty()) { 
			int front = q.front(); q.pop();
			for (int i = 0; i < map[front].size(); i++) {
				int e = map[front][i];
				if (c[front][e] - f[front][e] > 0 && visit[e] == -1) {
					// 흐를 수 있다 && 이번 BFS에서 흐른적이 없다.
					q.push(e); 
					visit[e] = front; // front에서 e로 흘렀다는 것을 알려준다.
					if (e == end) break; // end 점을 찾게되면 flow 중지
				}
			}
		}
		// 모든 경로를 찾은 경우
		if (visit[end] == -1) break; // BFS에서 end 점을 방문하지 않았다 -> 더이상 흐르는 경우가 없다.
		int flow = INT_MAX; // 최솟값을 찾기 위함
		for (int i = end; i != start; i = visit[i]) {
			flow = min(flow, c[visit[i]][i] - f[visit[i]][i]);
		}

		for (int i = end; i != start; i = visit[i]) {
			// 음의 간선이 있다고 생각함으로써 모든 경우의 수를 탐색 할 수 있게 된다.
			f[visit[i]][i] += flow;
			f[i][visit[i]] -= flow;
		}
		ret += flow;
	}
	return ret;
}

int getID(char c) {
	int ret;
	if (c >= 97) {
		ret = c - 97;
		ret += 26;
	}
	else {
		ret = c - 65;
	}
	return ret;
}

int main() {
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int N; cin >> N;
	for (int i = 0; i < N; i++) {
		char a, b; int w; cin >> a >> b >> w;
		int A = getID(a);
		int B = getID(b);
		//cout << a << " : " << A << " " << b << " : "<< B << endl;
		map[A].push_back(B);
		map[B].push_back(A);
		c[A][B] += w;
		c[B][A] += w;
	}
	cout << maxFlow(0, 25) << "\n";
}

기타

'알고리즘 > [C++]BOJ' 카테고리의 다른 글

[백준BOJ] 1671 상어의 저녁식사  (0) 2019.10.14
[백준BOJ] 11376 열혈강호2  (0) 2019.10.13
[백준BOJ] 11375 열혈강호  (0) 2019.10.13

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

 

11376번: 열혈강호 2

강호네 회사에는 직원이 N명이 있고, 해야할 일이 M개가 있다. 직원은 1번부터 N번까지 번호가 매겨져 있고, 일은 1번부터 M번까지 번호가 매겨져 있다. 각 직원은 최대 두 개의 일을 할 수 있고, 각각의 일을 담당하는 사람은 1명이어야 한다. 각각의 직원이 할 수 있는 일의 목록이 주어졌을 때, M개의 일 중에서 최대 몇 개를 할 수 있는지 구하는 프로그램을 작성하시오.

www.acmicpc.net


문제해설

열혈강호 1 에서 바뀐 조건은 한사람이 2가지의 일을 할 수 있다는 것이다.

visit을 초기화 하고나서,

2번의 flow를 발생시키면 된다.


전체코드

#include <iostream>
#include <vector>
#include <stack>
#include <queue>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define endl '\n'
//--------------------------------------------------------------------------------------

#define MAX_N 1001
vector<int> map[MAX_N];

int employee[MAX_N];
int work[MAX_N];
bool visit[MAX_N];

bool fun(int s) {
	visit[s] = true;
	for (int i = 0; i < map[s].size(); i++) {
		if (work[map[s][i]] == -1 || (!visit[work[map[s][i]]] && fun(work[map[s][i]]))) {
			employee[s] = map[s][i];
			work[map[s][i]] = s;
			return true;
		}
	}
	return false;
}

int main() {
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int N, M; cin >> N >> M;

	for (int i = 1; i <= N; i++) {
		int s; cin >> s;
		for (int j = 0; j < s; j++) {
			int data; cin >> data;
			map[i].push_back(data);
		}
	}
	memset(employee, -1, sizeof(employee));
	memset(work, -1, sizeof(work));
	int ans = 0;
	for (int i = 1; i <= N; i++) {
		if (employee[i] == -1) {
			memset(visit, false, sizeof(visit));
			if (fun(i)) ans++;
			if (fun(i)) ans++;
		}
	}
	cout << ans << endl;
}

 


기타

 

'알고리즘 > [C++]BOJ' 카테고리의 다른 글

[백준BOJ] 6086 최대 유량  (0) 2019.10.13
[백준BOJ] 11375 열혈강호  (0) 2019.10.13
[백준BOJ] 2188 축사 배정  (0) 2019.10.13

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

 

11375번: 열혈강호

강호네 회사에는 직원이 N명이 있고, 해야할 일이 M개가 있다. 직원은 1번부터 N번까지 번호가 매겨져 있고, 일은 1번부터 M번까지 번호가 매겨져 있다. 각 직원은 한 개의 일만 할 수 있고, 각각의 일을 담당하는 사람은 1명이어야 한다. 각각의 직원이 할 수 있는 일의 목록이 주어졌을 때, M개의 일 중에서 최대 몇 개를 할 수 있는지 구하는 프로그램을 작성하시오.

www.acmicpc.net


문제해설

밑의 문제와 동일한 문제입니다( 입력 범위만 살짝 다릅니다 )

2019/10/13 - [알고리즘/BOJ] - [백준BOJ] 2188 축사 배정

 

[백준BOJ] 2188 축사 배정

https://www.acmicpc.net/problem/2188 2188번: 축사 배정 농부 존은 소 축사를 완성하였다. 축사 환경을 쾌적하게 유지하기 위해서, 존은 축사를 N개의 칸으로 구분하고, 한 칸에는 최대 한 마리의 소만 들어가게..

polohee81.tistory.com

 


전체코드

#include <iostream>
#include <vector>
#include <stack>
#include <queue>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define endl '\n'
//--------------------------------------------------------------------------------------

#define MAX_N 1001
vector<int> map[MAX_N];

int cow[MAX_N];
int area[MAX_N];
bool visit[MAX_N];

bool fun(int s) {
	visit[s] = true;
	for (int i = 0; i < map[s].size(); i++) {
		if (area[map[s][i]] == -1 || (!visit[area[map[s][i]]] && fun(area[map[s][i]]))) {
			cow[s] = map[s][i];
			area[map[s][i]] = s;
			return true;
		}
	}
	return false;
}

int main() {
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int N, M; cin >> N >> M;
	
	for (int i = 1; i <= N; i++) {
		int s; cin >> s;
		for (int j = 0; j < s; j++) {
			int data; cin >> data;
			map[i].push_back(data);
		}
	}
	memset(cow, -1, sizeof(cow));
	memset(area, -1, sizeof(area));
	int ans = 0;
	for (int i = 1; i <= N; i++) {
		if (cow[i] == -1) {
			memset(visit, false, sizeof(visit));
			if (fun(i)) ans++;
		}
	}
	cout << ans << endl;
}

기타

 

'알고리즘 > [C++]BOJ' 카테고리의 다른 글

[백준BOJ] 11376 열혈강호2  (0) 2019.10.13
[백준BOJ] 2188 축사 배정  (0) 2019.10.13
[BOJ백준] 10000 원 영역  (0) 2019.10.13

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

 

2188번: 축사 배정

농부 존은 소 축사를 완성하였다. 축사 환경을 쾌적하게 유지하기 위해서, 존은 축사를 N개의 칸으로 구분하고, 한 칸에는 최대 한 마리의 소만 들어가게 계획했다. 첫 주에는 소를 임의 배정해서 축사를 운영했으나, 곧 문제가 발생하게 되었다. 바로 소가 자신이 희망하는 몇 개의 축사 외에는 들어가기를 거부하는 것이다. 농부 존을 도와 최대한 많은 수의 소가 축사에 들어갈 수 있도록 하는 프로그램을 작성하시오. 축사의 번호는 1부터 M까지 매겨져 있다.

www.acmicpc.net


문제해설

N의 입력 범위가 작기 때문에, 소를 계속 가능한 위치에 이동시켜주면서 풀어 나가면 됩니다.

또한, 네트워크 플로우의 특성인 어느 소부터 시작하던지, 결국 되는경우는 모두 처리가 되기 때문에,

1번 소부터 N번 소까지 차레대로 처리해주면 됩니다.

k번 소를 원하는 지역에 넣으려고 하는 경우 두가지 케이스가 있습니다

case 1 ) 해당 지역이 비어있는 경우

case 2 ) 다른소가 차지하고 있는 경우

 

< case 1 >

해당 지역에 k번째 소를 넣어주고

k번째 소가 어느위치에 있는지도 알고 있어야 합니다.

 

< case 2 >

해당 지역에 있는 소를 y소라고 한다면,

y소를 다른위치로 옮겨야 합니다.

재귀 함수를 사용해서, y소를 다른 지역에 옮길 수 있는지 확인한뒤

(옮긴 후) k번째 소를 해당 지역에 넣어 주면 됩니다.

 


전체코드

#include <iostream>
#include <vector>
#include <stack>
#include <queue>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <string>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define endl '\n'
//--------------------------------------------------------------------------------------

#define MAX_N 201
vector<int> map[MAX_N];

int cow[MAX_N];
int area[MAX_N];
bool visit[MAX_N];

bool fun(int s) {
	// s번 소는 방문
	visit[s] = true;
	for (int i = 0; i < map[s].size(); i++) {
		// 해당 지역이 비었거나
		// 그렇지 않다면
		// 현재 가려고 하는 위치의 소가 처리 되있지 않고
		// &&
		// 처리 되지 않은소가 다른 지역으로 옮겨갔다면 ( true가 return 된다 )
		if (area[map[s][i]] == -1 || (!visit[area[map[s][i]]] && fun(area[map[s][i]]))) {
			cow[s] = map[s][i];
			area[map[s][i]] = s;
			return true;
		}
	}
	// 소를 현재 위치에서 이사시킬 수 없는 경우
	return false;
}

int main() {
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int N, M; cin >> N >> M;
	
	for (int i = 1; i <= N; i++) {
		int s; cin >> s;
		for (int j = 0; j < s; j++) {
			int data; cin >> data;
			map[i].push_back(data);
		}
	}
	memset(cow, -1, sizeof(cow));
	memset(area, -1, sizeof(area));
	int ans = 0;
	for (int i = 1; i <= N; i++) {
		// 1번 소 부터 차례대로 넣는다 ( 플로우 형식이기 때문에 그냥 차례대로 확인해도 상관없다 )
		if (cow[i] == -1) {
			memset(visit, false, sizeof(visit));
			if (fun(i)) ans++;
		}
	}
	cout << ans << endl;
}

기타

'알고리즘 > [C++]BOJ' 카테고리의 다른 글

[백준BOJ] 11375 열혈강호  (0) 2019.10.13
[BOJ백준] 10000 원 영역  (0) 2019.10.13
[백준BOJ] 3078 좋은 친구  (2) 2019.10.10

+ Recent posts