www.acmicpc.net/problem/16236

 

16236번: 아기 상어

N×N 크기의 공간에 물고기 M마리와 아기 상어 1마리가 있다. 공간은 1×1 크기의 정사각형 칸으로 나누어져 있다. 한 칸에는 물고기가 최대 1마리 존재한다. 아기 상어와 물고기는 모두 크기를 가

www.acmicpc.net

 

1. 풀이방법

 

- BFS 문제. DFS로도 풀어보았지만 로직은 맞는 것 같지만 역시나 시간 초과가 나옵니다. (코드는 첨부)

 

- 상어의 위치로 부터 조건에 가장 부합하는 물고기 위치를 BFS로 탐색해 찾아낸 다음

 

- 그 위치의 물고기를 먹고 상어의크기를 조정이 필요할 경우 해주고 그 위치로 아기상어를 옮겨 주시면 됩니다.

 

- 저는 DFS를 먼저 풀고 거기서 재활용할 함수들은 재활용하고 makefishcase만 bfs로 바꿔서 구현하여서,

 

- 불필요한 부분도 들어있습니다. 깔끔하지 않습니다.

 

 

 

2. 주의사항

 

- 한가지 있는데, 저와 같은 케이스인 분들을 위해 기록합니다.

 

- bfs로 짰는데 "시간초과" 라는 결과를 얻었습니다.

 

- 코드를 아무리 봐도 시간초과가 나올만한 연산을 하는 곳이 없습니다.

 

- 이럴경우 거의 십중팔구 무한루프에 빠지는 테스트케이스가 존재한다는 뜻입니다.

 

- 그것을 곰곰히 생각해보니, 

 

- 이러한 경우 입니다

 

  9 0 0

  4 4 4

  0 0 1

 

-저의 코드에서는 전체 바다에서 먹을 수 있는 물고기가 존재하는지 체크하는 함수가 있는데,

 

- 그 함수는 이러한 케이스에서 1이라는 물고기가 있으므로 break;를 걸지 않아서 무한루프에 빠져

 

- 시간초과가 나오는 것이였습니다.

 

- 물론 종료조건 함수를 훨씬 정교하고 예쁘게 만드셨다면 해당되지 않으실 수 도 있습니다!

 

3. 나의코드

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

int dx[4] = { 1,-1,0,0 }; //상하좌우 4방향 탐색
int dy[4] = { 0,0,-1,1 };
int sea[21][21];
bool visit[21][21];
int N;
int resultsecond;
int tmpeat = 0;
int sharksize = 2;
int sharkx, sharky;
int failsecond = -1;

struct fish {
	int x, y, far;
};
vector<fish> farr;
void inputs() {
	cin >> N;
	for (int i = 0; i < N; i++) {
		for (int j = 0; j < N; j++) {
			cin >> sea[i][j];
			if (sea[i][j] == 9) { sharkx = i; sharky = j; }
		}
	}
}
bool checksea() {
	for (int i = 0; i < N; i++) {
		for (int j = 0; j < N; j++) {
			if (sea[i][j] > 0 && sea[i][j] < sharksize && sea[i][j] != 9) { return false; }
		}
	}
	return true; //더이상 잡아 먹을 것 이 없음
}
void visitclear() {
	for (int i = 0; i < N; i++) {
		for (int j = 0; j < N; j++) {
			visit[i][j] = false;
		}
	}
}
queue<fish> tmpq;
void makefishcase(int x, int y) {
	tmpq.push({ x,y,0 });
	int cnt = 0;
	while (1) {
		int si = tmpq.size();
		if (si == 0) { failsecond = resultsecond; break; }
		while (si--) {
			int tx = tmpq.front().x;
			int ty = tmpq.front().y;
			tmpq.pop();
			if (sea[tx][ty] > 0 && sea[tx][ty] < sharksize && sea[tx][ty] != 9) {
				farr.push_back({ tx,ty,cnt });
			}
			for (int i = 0; i < 4; i++) {
				int nextx = tx + dx[i]; int nexty = ty + dy[i];
				if (nextx >= 0 && nextx < N && nexty >= 0 && nexty < N) {
					if (visit[nextx][nexty] == false && sea[nextx][nexty] <= sharksize) {
						visit[nextx][nexty] = true; 
						tmpq.push({ nextx,nexty,0 }); 
					}
				}
			}
		}
		if (farr.empty() == false) break;
		cnt++;
	}
}
bool compare(fish& lh, fish& rh) {
	if (lh.far < rh.far) return true;
	else if (lh.far == rh.far) {
		if (lh.x < rh.x) return true;
		else if (lh.x == rh.x) {
			if (lh.y < rh.y) return true;
			else return false;
		}
		else return false;
	}
	else return false;
}
void queuereset() {
	while (!tmpq.empty()) {
		tmpq.pop();
	}
}
void eatfish() {
	queuereset();
	makefishcase(sharkx, sharky);
	if (failsecond != -1) return;
	sort(farr.begin(), farr.end(), compare); //거리 순 > 위에있는순> 왼쪽에 있는순으로 소팅
	sea[sharkx][sharky] = 0;
	sharkx = farr[0].x; sharky = farr[0].y;
	tmpeat++;
	if (sharksize == tmpeat) {
		tmpeat = 0; sharksize++;
	}
	sea[sharkx][sharky] = 9;
	resultsecond += farr[0].far;
	visitclear();
	farr.clear();
}
int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	inputs();
	while (1) {
		if (checksea()) break;
		visit[sharkx][sharky] = true;
		eatfish();
		if (failsecond != -1) break;
	}
	if (failsecond == -1) {
		cout << resultsecond << "\n";
	}
	else { cout << failsecond << "\n"; }
	return 0;
}

'알고리즘 문제풀이 > DFS와 BFS' 카테고리의 다른 글

백준 19238 [C++]  (0) 2021.01.17
백준 17142 [C++]  (0) 2021.01.17
백준 2644 [C++]  (0) 2020.12.22
백준 7569 [C++]  (1) 2020.12.20
백준 1697 [C++]  (0) 2020.12.20

www.acmicpc.net/problem/20061

 

20061번: 모노미노도미노 2

모노미노도미노는 아래와 같이 생긴 보드에서 진행되는 게임이다. 보드는 빨간색 보드, 파란색 보드, 초록색 보드가 그림과 같이 붙어있는 형태이다. 게임에서 사용하는 좌표 (x, y)에서 x는 행,

www.acmicpc.net

1. 풀이방법

 

- 문제의 조건을 그대로 구현하는 문제입니다.

 

 

- 문제가 길긴 하지만 테스트케이스 와 문제자체(그림)이 친절하게 나와있어서 과정을 체크하기 편했습니다.

 

 

- 딱히 알고리즘을 사용할 건 없고 조건에 맞춘 구현만 해주시면 됩니다.

 

 

2. 주의사항

 

- 두번정도 틀린 후에 정답을 맞췄는데, 첫번째 오답의 경우 도미노를 밀 때 효율적으로 짠다셈 치고 뒤에서 부터

 

 

- 탐색을 하면서 도형의 자리를 찾았는데 , 오답이 뜬후 디버깅하며 지도 형태를 보니

 

 

- (이러한 경우)

 

 

- 0 0 0 1                    1 0 0 1

  0 1 0 1        (--->)   1 1 0 1  (정답)

 

 

 이렇게 되어야 하는데,

 

 

                                 0 0 1 1

                  (--->)     0 1 1 1 (오답)

 

 

- 이러한 실수를 했어서, 앞쪽부터 탐색하는 것으로 바꾸었습니다. (앞으로도 이런문제에서 주의하자..!!)

 

 

- 두번째 오답은 도형 유형이 3가지 있는데 테트리스를 생각해 보시면 이게 두줄이 한번에 clear 되는 경우가 발생할 수

 

 

- 있습니다. 저 같은 경우 코드 상에서 뒤쪽 부터 탐색 하며 한 행 또는 열 이 클리어 된다면 앞쪽에서 한칸씩 당겨오는

 

 

- 방식으로 구현했는데, 이렇게 구현할 경우 땡겨온 다음 그 행 또는 렬 을 한번 더 체크 해주어야 합니다 ( i++ )

 

 

- 자세한 것은 주석을 확인해주세요

 

 

 

3. 나의코드

 

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


int N,t,x,y;
int ground[10][10]; //0~9
int resultscore;
int resultblock;

void fillnemo() {
	if (t == 1) {
		for (int i = 4; i <=9; i++) { //파란색
			if (i == 9) { ground[x][i] = 1; break; }
			if (ground[x][i+1] == 1) { ground[x][i] = 1; break; }
		}
		for (int i = 4; i <= 9; i++) { //초록색
			if (i == 9) { ground[i][y] = 1; break; }
			if (ground[i+1][y] == 1) { ground[i][y] = 1; break; }
		}
	}
	else if (t == 2) {
		int tmpindex=10;
		for (int i = 4; i <= 9; i++) { //파란색
			if (ground[x][i] == 1 ) {
				tmpindex = i;
				break; }
		}
		ground[x][tmpindex - 1] = 1; ground[x][tmpindex - 2] = 1;

		tmpindex = 10;
		for (int i = 4; i <= 9; i++) { //초록색
			if (ground[i][y] == 1||ground[i][y+1]==1) {
				tmpindex = i;
				break;
			}
		}
		ground[tmpindex - 1][y] = 1; ground[tmpindex - 1][y + 1] = 1;
	}
	else {
		int tmpindex = 10;
		for (int i = 4; i <= 9; i++) { //파란색
			if (ground[x][i] == 1 || ground[x + 1][i] == 1) {
				tmpindex = i;
				break;
			}
		}
		ground[x][tmpindex - 1] = 1; ground[x + 1][tmpindex - 1] = 1;
		tmpindex = 10;
		for (int i = 4; i <= 9; i++) { //초록색
			if (ground[i][y] == 1 ) {
				tmpindex = i;
				break;
			}
		}
		ground[tmpindex - 1][y] = 1; ground[tmpindex - 2][y] = 1;
	}
}

void delblock() {
	//파란색
	for (int i = 9; i >= 6; i--) {
		bool checking = false;
		for (int j = 0; j < 4; j++) {
			if (ground[j][i] == 0) { checking = true; break; }
		}
		if (checking == false) { //4블록이 꽉찬경우
			resultscore++;
			for (int k = i; k >= 4; k--) { //이동시키기
				for (int l = 0; l < 4; l++) { ground[l][k] = ground[l][k-1]; }
			}
			i++; // 두줄이 한번에 꽉차는 경우를 위해서 이동시킨후 다시 그 행,렬부터 확인해야됨
		}
	}
	//초록색
	for (int i = 9; i >= 6; i--) {
		bool checking = false;
		for (int j = 0; j < 4; j++) {
			if (ground[i][j] == 0) { checking = true; break; }
		}
		if (checking == false) {
			resultscore++;
			for (int k = i; k >= 4; k--) { //이동시키기
				for (int l = 0; l < 4; l++) { ground[k][l] = ground[k-1][l]; }
			}
			i++; // 두줄이 한번에 꽉차는 경우를 위해서 이동시킨후 다시 그 행,렬부터 확인해야됨
		}
	}
}

void moveblock() { 
	//연한 파란색
	bool checking = false;
	int checkingcount = 0;
	for (int i = 4; i <= 5; i++) {
		for (int j = 0; j < 4; j++) {
			if (ground[j][i] == 1) { checkingcount++; break; }
		}
	}
	if (checkingcount != 0) {
		for (int i = 9; i >= 4; i--) {
			for (int j = 0; j < 4; j++) {
				ground[j][i] = ground[j][i - checkingcount];
			}
		}
	}
	for (int i = 4; i <= 5; i++) {
		for (int j = 0; j < 4; j++) {
			ground[j][i] = 0;
		}
	}
	//연한 초록색
	checking = false;
	checkingcount = 0;
	for (int i = 4; i <= 5; i++) {
		for (int j = 0; j < 4; j++) {
			if (ground[i][j] == 1) { checkingcount++; break; }
		}
	}
	if (checkingcount != 0) {
		for (int i = 9; i >= 4; i--) {
			for (int j = 0; j < 4; j++) {
				ground[i][j] = ground[i-checkingcount][j];
			}
		}
	}
	for (int i = 4; i <= 5; i++) {
		for (int j = 0; j < 4; j++) {
			ground[i][j] = 0;
		}
	}
}
void watchground() {
	cout << endl;
	for (int i = 0; i <= 9; i++) {
		for (int j = 0; j <= 9; j++) {
			cout << ground[i][j];
		}
		cout << endl;
	}
}
void getblock() {
	for (int i = 6; i <= 9; i++) {
		for (int j = 0; j < 4; j++) {
			if (ground[j][i] == true) resultblock++;
		}
	}
	for (int i = 6; i <= 9; i++) {
		for (int j = 0; j < 4; j++) {
			if (ground[i][j] == true) resultblock++;
		}
	}
}

int main() {
	ios_base::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
	cin >> N;
	while (N--) {
		cin >> t >> x >> y;
		fillnemo(); // 블록 초록색 파란색으로 이동시키기
		delblock(); // 4블록이 꽉찬 행,또는 렬 삭제 (점수 획득)
		moveblock(); // 연한초록, 연한파란색에 블록이 있을 경우 이동
	}
	getblock();
	cout << resultscore << "\n";
	cout << resultblock << "\n";

	return 0;
}

 

'알고리즘 문제풀이 > 구현' 카테고리의 다른 글

백준 2726 [C++]  (0) 2021.01.16
백준 10993 [C++]  (0) 2021.01.16
백준 20056 [C++]  (0) 2020.12.23
백준 20055 [C++]  (0) 2020.12.23
백준 13458 [C++]  (0) 2020.12.15

www.acmicpc.net/problem/17135

 

17135번: 캐슬 디펜스

첫째 줄에 격자판 행의 수 N, 열의 수 M, 궁수의 공격 거리 제한 D가 주어진다. 둘째 줄부터 N개의 줄에는 격자판의 상태가 주어진다. 0은 빈 칸, 1은 적이 있는 칸이다.

www.acmicpc.net

 

 

1.풀이방법

 

 

- 3명의 궁수가 (N,0) (N,M-1)까지 배치될 수 있는 모든 CASE를 생성하여 게임을 시작합니다. (조합)

 

 

- 격자에서 모든 적이 사라지면 게임을 종료

 

 

- 3명의 궁수를 대상으로 모든 적들의 거리를  계산후 좌표와 거리를 구조체로 선언하여, eset(enemy set)에 넣고

 

 

- sort를 진행(거리가 가까운 순, 같으면 좌표 y가 더 작은 순(왼쪽))

 

 

 

 

 

2. 주의사항

 

 

- 조합만 잘 구현하면 문제는 조건대로 찬찬히 구현하면 해결 완료.

 

 

 

 

 

3.나의코드

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

int ground[17][17];
int copyground[17][17];
bool selected[17];
vector<pair<int, int>> archer;
int maxresult = -1;
int N, M;
int D;
int enemycount;
struct enemy { int x; int y; int d; };

bool cmp(enemy a, enemy b) {
	if (a.d < b.d) { return true; }
	else {
		if (a.d == b.d) {
			if (a.y < b.y) { return true; }
			else { return false; }
		}
		return false;
	}
}

void inputs() {
	cin >> N >> M >> D;
	for (int i = 0; i < N; i++) {
		for (int j = 0; j < M; j++) {
			cin >> ground[i][j];
			copyground[i][j] = ground[i][j];
		}
	}
}
void groundinit() {
	for (int i = 0; i < N; i++) {
		for (int j = 0; j < M; j++) {
			ground[i][j] = copyground[i][j];
		}
	}
}
bool checkground() {
	for (int i = 0; i < N; i++) {
		for (int j = 0; j < M; j++) {
			if (ground[i][j] == 1) { return false; }
		}
	}
	return true;
}
int abs(int n1, int n2) {
	if (n1 > n2) { return n1 - n2; }
	else return n2 - n1;
}
int getdistance(int x, int y, int x2, int y2) {
	return (abs(x, x2) + abs(y, y2));
};
void enemymove() {
	for (int i = N - 1; i >= 0; i--) {
		for (int j = 0; j < M; j++) {
			if (ground[i][j] == 1) {
				ground[i + 1][j] = 1; ground[i][j] = 0;
			}
		}
	}
}
void startgame() {
	while (1) {// 반복한번 = 1턴
		if (checkground() == true) { 
			if (maxresult < enemycount) { maxresult = enemycount; }
			return;
		}
		vector<enemy> eset[3];
		for (int i = 0; i < archer.size(); i++) {
			for (int a = 0; a < N; a++) {
				for (int b = 0; b < M; b++) {
					if (ground[a][b] == 1) {
						if (getdistance(archer[i].first, archer[i].second, a, b) <= D) {
							enemy tmp; tmp.x = a; tmp.y = b; tmp.d = getdistance(archer[i].first, archer[i].second, a, b);
							eset[i].push_back(tmp); //1명의 궁수당 적이 들어있음 거리와 함께
						}
					}
				}
			}
			sort(eset[i].begin(), eset[i].end(), cmp);
		}
		//각각의 궁수에 대해서 0번쨰 인덱스의 적이 죽을 차례
		for (int i = 0; i < archer.size(); i++) {
			if (eset[i].empty() == true) continue; //사정거리내의 적이 없음
			if (ground[eset[i][0].x][eset[i][0].y] == 1) {
				ground[eset[i][0].x][eset[i][0].y] = 0; //적을 없앤다
				enemycount++;
			}
		}
		enemymove();
	}
}


void makecase(int index,int num) {
	if (num == 3) {
		enemycount = 0;
		groundinit();
		startgame(); return;
	}
	pair<int, int> tmp;
	tmp.first = N;
	for (int i = index; i < M; i++) {
		if (selected[i] == true  ) continue;
		tmp.second = i;
		archer.push_back(tmp);
		selected[i] = true;
		makecase(i,num + 1);
		archer.pop_back();
		selected[i] = false;
	}
}

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

	inputs();
	makecase(0,0);
	cout << maxresult;

	return 0;
}

'알고리즘 문제풀이 > 완탐' 카테고리의 다른 글

백준 1107 [C++]  (0) 2020.12.14
백준 14500 [C++]  (0) 2020.12.05
백준 17136 [C++]  (0) 2020.10.20
백준 16637 [C++]  (0) 2020.10.19
백준 17070 [C++]  (0) 2020.10.14

+ Recent posts