문제출처: https://www.acmicpc.net/problem/2589


풀이

1.육지인 좌표에서 bfs를 돌리고

2. 그 좌표에서 bfs를 돌렸을 때 최단거리를 구함

3. 모든 육지좌표에서 1,2를 수행하며 최단거리 갱신

4. 마지막에 구한 최단거리==정답


핵심: 어떤 육지좌표에서 1,2를 수행하고 다른 육지좌표에서 최단거리를 구할 때 check배열은 0으로 초기화(방문하지않았다고)해야합니다.

그리고 정답을 출력할 때, 최단거리에서 1을 빼준값을 출력해야합니다. 아래 그림을 참조하면됩니다.

당연하지만, bfs가 아래 그림처럼 최단거리로 갱신해주기 때문에  최단거리문제에서 dfs보다 bfs가 훨씬유리합니다.



코드

#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
char map[50][50];
int check[50][50];
int dx[4] = { -1,1,0,0 };
int dy[4] = { 0,0,-1,1 };
int n, m;
int cnt;
int max_value = 0;
void bfs(int y, int x);
int main()
{
	cin.tie(0);
	cin.sync_with_stdio(false);
	cin >> n >> m;
	for (int i = 0; i < n; i++)
		cin >> map[i];
	
	//육지일때 그 좌표에서 최단거리구해봄
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < m; j++)
		{
			if (map[i][j] == 'L')
				//육지에서시작되는 각 좌표들로부터의 최단거리를
			{
				bfs(i, j);
				max_value = max(max_value, cnt);
				cnt = 0;
			}
		}
	}
	//육지에서 bfs를돌리면 그 육지의 좌표의 인접한 좌표가2가되므로 하나빼줘야됨
	cout << max_value-1 << '\n';
}
void bfs(int y, int x)
{
	//핵심! 육지인 모든곳을 탐색하므로 한번 탐색할때마다 갱신
	memset(check, 0, sizeof(check));
	queue<pair<int, int>>q;
	q.push({ y,x });
	check[y][x] = 1;
	while (!q.empty())
	{
		y = q.front().first;
		x = q.front().second;
		q.pop();
		for (int i = 0; i < 4; i++)
		{
			int ny = y + dy[i];
			int nx = x + dx[i];
			//좌표범위내에서 확인
			if (nx >= 0 && ny >= 0 && nx < m && ny < n)
			{
				//만약 육지이고, 만나지않았떤길이면
				if (map[ny][nx] == 'L' && check[ny][nx] == 0)
				{
					check[ny][nx] = check[y][x] + 1;
					cnt = max(cnt, check[ny][nx]);
					q.push({ ny,nx });
				}
				
			}
		}
	}
}


결과


'문제풀이(BOJ) > DFS,BFS' 카테고리의 다른 글

[백준 1759] 암호 만들기  (0) 2020.02.06
[백준] 촌수계산  (0) 2020.01.20
[백준 14502] 연구소 (BFS)  (0) 2020.01.17
[백준 2667] 단지번호붙이기 (BFS)  (0) 2020.01.17
[백준 11724] 연결 요소의 개수(DFS)  (0) 2020.01.15

문제출처: 

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


풀이

제 코드의 실행시간이 잘 푸신분들에 비해 최대 6배 차이가량납니다. 이해가 어려워서 이 글을 보러오신거면 제 코드를 보고 다른 빠른풀이법을 보시면좋을것같아요.(그분들 코드가 많이 어렵더라구요..) 

제 코드상에서 실행시간을 줄일 수 있을만한 요소에 대해 알고계신분들은 댓글로 지적좀해주세요..


대충 머릿속으로는 구상이되는데 막상 구현할때 벽3개를놓고 연산하는 시뮬레이션과정이 너무어려워서 다른분들의 코드를 조금 참조했습니다...

백트래킹 기법도 쓰이는데, 제 생각에 dfs,bfs문제중에 어려운문제는 거의 백트래킹+브루트포스+dfs or bfs 같네요.. 어렵습니다.

글로써봤자 이해가 안가니 주석과 코드를 보고 흐름을 따라가보시면 이해가편할 것 같습니다.


#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
//벽을 세움
//벽3개를세움
//벽3개를 세울 때 bfs,
//그 bfs를돌렷을때 각 최댓값비교
int map[8][8];
int temp[8][8];
int dx[4] = { 0,0,1,-1 };
int dy[4] = { -1,1,0,0 };
int n, m, area;
void make_wall(int wall);
void bfs();
int main()
{
	cin.tie(0);
	cin.sync_with_stdio(false);
	cin >> n >> m;
	for (int i = 0; i < n; i++)
		for (int j = 0; j < m; j++)
			cin >> map[i][j];

	//비어있는공간있을떄 그떄 temp로복사받음
	//비어있는 공간,그 자리에서부터 벽3개를 놓는 시늉을 다 돌림
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < m; j++)
		{
			if (map[i][j] == 0)
			{
				for (int i = 0; i < n; i++)
					for (int j = 0; j < m; j++)
						temp[i][j] = map[i][j];

				//현재 자리에 벽세움
				temp[i][j] = 1;
				make_wall(1);
				//다시원상복구
				temp[i][j] = 0;
			}
			
		}
	}
	cout << area << '\n';
}
void make_wall(int wall)
{
	//벽3개 만들었으면 bfs돌림
	if (wall == 3)
	{
		bfs();
		return;
	}
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < m; j++)
		{
			//좌표가 0이면
			if (temp[i][j] == 0)
			{
				//벽을 세우고
				temp[i][j] = 1;
				//벽을 한칸늘렸으니 3개인지확인
				make_wall(wall + 1);
				//위의함수로 그자리에서 모든연산끝났으니 다시 0으로,
				temp[i][j] = 0;
			}
		}
	}
}
void bfs()
{
	//temp변수로 갖고놀면 virus가 퍼질떄 temp가 수정되므로 다시 배열복사
	int virus[8][8];
	for (int i = 0; i < n; i++)
		for (int j = 0; j < m; j++)
			virus[i][j] = temp[i][j];

	queue<pair<int, int>>q;
	//바이러스좌표를 저장하여 퍼뜨림
	for (int i = 0; i < n; i++)
		for (int j = 0; j < m; j++)
			if (virus[i][j] == 2)
				q.push({ i,j });

	//퍼뜨려서,
	//최종적으로 map에서 0인 현재좌표에서
	//벽3개를 가정하고, 퍼뜨렸을 때 최대안전영역구함
	while (!q.empty())
	{
		int y = q.front().first;
		int x = q.front().second;
		q.pop();
		for (int i = 0; i < 4; i++)
		{
			int ny = y + dy[i];
			int nx = x + dx[i];
			if (nx >= 0 && ny >= 0 && nx < m && ny < n)
			{
				if (virus[ny][nx] == 0 )
				{
					virus[ny][nx] = 2;
					q.push({ ny,nx });
				}
			}
		}
	}
	//최대영역구하기
	int cnt = 0;
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < m; j++)
		{
			if (virus[i][j] == 0)
				cnt++;
		}
	}
	area = max(area, cnt);
}

결과



'문제풀이(BOJ) > DFS,BFS' 카테고리의 다른 글

[백준] 촌수계산  (0) 2020.01.20
[백준 2589] 보물섬(BFS)  (0) 2020.01.17
[백준 2667] 단지번호붙이기 (BFS)  (0) 2020.01.17
[백준 11724] 연결 요소의 개수(DFS)  (0) 2020.01.15
[백준 1012] 유기농 배추 (DFS)  (0) 2020.01.15

문제출처: https://www.acmicpc.net/problem/2667


풀이

연결요소의 갯수와 bfs / dfs를 했을 때 그 연결요소의 거리를 구하면 되는 탐색문제입니다.

n=25일때 단지의 갯수는 최대 25*25개이므로, 단지의 갯수를 담을수있는 1차원배열과, 단지의 갯수,번호의 갯수를 나타낼수 있게 cnt, index변수만 선언하여 연산하면됩니다. 주석에 상세히 설명을 해놨으니 참고해주세요.


코드

#include<iostream>
#include<queue>
#include<algorithm>
using namespace std;
char map[25][25];
int check[25][25];
//단지는 1차원배열로선언,최악의경우 n=5일때 모든칸이 1로채워질때
int danji[25 * 25 + 1];
int dx[4] = { -1,1,0,0 };
int dy[4] = { 0,0,-1,1 };
int n;
int cnt;
int index;
void bfs(int y, int x);

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

	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < n; j++)
		{
			//길이있고, 방문하지않은 노드면?
			if (map[i][j] == '1' && check[i][j] == 0)
			{
				//그 자리서부터 연산돌림
				bfs(i, j);
				//단지의 갯수, 그 단지의마을갯수를 구하기위한 선언
				danji[index++] = cnt;
				cnt = 0;
			}
		}
	}
	//단지의 마을갯수가적은순으로정렬
	sort(danji, danji+index);
	//단지갯수
	cout << index <<  endl;

	for (int i = 0; i < index; i++)
		cout << danji[i] << endl;
}
void bfs(int y, int x)
{
	queue<pair<int, int>>q;
	q.push({ y,x });
	//까먹지말자 check 
	check[y][x] = 1;
	//일단 bfs조건을 맞추고왔으므로 카운팅
	cnt++;
	while (!q.empty())
	{
		y = q.front().first;
		x = q.front().second;
		//까먹지말자pop
		q.pop();
		for (int i = 0; i < 4; i++)
		{
			int ny = y + dy[i];
			int nx = x + dx[i];
			//범위내에있으며, 단지가있고 방문하지않은곳이면
			if (ny >= 0 && nx >= 0 && ny < n && nx < n) {
				if (map[ny][nx] == '1' && check[ny][nx] == 0)
				{
					q.push({ ny,nx });
					check[ny][nx] = 1;
					//카운팅
					cnt++;
				}
			}
		}
	}
}


결과

문제출처: https://www.acmicpc.net/problem/5567


그래프구현문제입니다.


풀이

보기의 예제를 보면, 1과 연결되있는 노드중에서 그 노드와 연결되어있는 노드의 개수를 찾는 문제입니다.

사실 이것만 구현하면 끝입니다....설명은 소스코드로..


코드

#include<iostream>
using namespace std;
int map[501][501];
int check[501];
int n, m,a,b, cnt;
int main()
{
	cin >> n >> m;
	for (int i = 0; i < m; i++)
	{
		cin >> a >> b;
		map[a][b] = 1;
		map[b][a] = 1;
	}
	for (int i = 2; i <= n; i++)
	{
		//만약 1과연결되있으면
		if (map[1][i])
		{
			//방문처리
			check[i] = 1;
			for (int j = 2; j <= n; j++)
			{
				//그 노드에서 연결된 노드를 방문처리
				if (map[i][j] == 1)
					check[j] = 1;
			}
		}
	}
	for (int i = 2; i <= n; i++)
	{
		//1되어있는 노드=1과연결되있는 노드와 연결되어있는 노드 수
		if (check[i] == 1)
			cnt++;
	}
	cout << cnt << endl;
}


결과

'문제풀이(BOJ) > 시뮬레이션(구현)' 카테고리의 다른 글

[백준 5533] 유니크  (0) 2020.01.19
[백준 9517] 아이 러브 크로아티아  (0) 2020.01.19
[백준 1120] 문자열  (0) 2020.01.09
[백준 2526] 싸이클  (0) 2020.01.09
[백준 11507] 카드셋트  (0) 2020.01.08

문제출처: https://www.acmicpc.net/problem/1652

 

분류는 수학으로되어있는데 규칙찾기문제같네요...

 

풀이

문제를 잘 이해하셔야합니다. 단순히 연속된 좌표가 '.'일때만 체크해주면 좋겟지만, 예외상황이 존재합니다.

대표적으로 ..XX..일때입니다.(세로표현도 똑같음.) 이 경우, 가로로보면 누울자리가 1개가 아니라 2개입니다. 이 예외만 처리해주면되고, 저 같은 경우 현재 좌표가 '.'일때, 'X'일때로 나눠서 row와 col값을 계산했습니다.

 

코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include<iostream>
using namespace std;
char map[100][100];
int n, cnt, row, col;
int main()
{
    cin >> n;
    for (int i = 0; i < n; i++)
        cin >> map[i];
 
    //가로부터.
    //'.'이면 cnt++
    //'x'만났는데 cnt>=2면 답+1,cnt=0;
    //'x'만나면 cnt=0;
    for (int i = 0; i < n; i++)
    {
        cnt = 0;
        for (int j = 0; j < n; j++)
        {
            //짐이없으면 일단 한칸증가
            //cnt>=2면 ans++
            if (map[i][j] == '.')
                cnt++;
            else if (map[i][j] == 'X')
            {
                if (cnt >= 2)
                {
                    row++;
                    cnt = 0;
                }
                else
                    cnt = 0;
            }
        }
        if (cnt >= 2)
            row++;
    }
    //세로도 똑같이
    for (int i = 0; i < n; i++)
    {
        cnt = 0;
        for (int j = 0; j < n; j++)
        {
            //짐이없으면 일단 한칸증가
            //cnt>=2면 ans++
            if (map[j][i] == '.')
                cnt++;
            else if (map[j][i] == 'X')
            {
                if (cnt >= 2)
                {
                    col++;
                    cnt = 0;
                }
                else
                    cnt = 0;
            }
        }
        if (cnt >= 2)
            col++;
    }
    cout << row << " " << col << endl;
}
cs

결과

'문제풀이(BOJ) > 규칙찾기' 카테고리의 다른 글

[백준 1676] 팩토리얼 0의 개수  (0) 2020.08.27
[백준 14954] Happy Number  (0) 2020.03.10
[백준 1855] 암호  (0) 2020.01.06
[백준 3076] 상근이의 체스판  (0) 2020.01.03
[백준 1551] 수열의 변화  (0) 2020.01.03

문제출처: https://www.acmicpc.net/problem/11724


풀이

그래프를 그려서 연결 요소의 개수를 세주면 됩니다. 무방향그래프이기 때문에 u->v, v->u 길을 모두 1처리해줘야합니다.


코드

#include<iostream>
using namespace std;
int map[1001][1001];
int check[1001];
int n, m, u, v, cnt;
void dfs(int x);
int main()
{
	cin.tie(0);
	cin.sync_with_stdio(false);
	cin >> n >> m;
	for (int i=0;i<m;i++)
	{
		cin >> u >> v;
		map[u][v] = 1;
		map[v][u] = 1;
	}
	for (int i = 1; i <= n; i++)
	{
		if(!check[i])
		{
			dfs(i);
			cnt++;
		}
	}
	cout << cnt << '\n';
}
void dfs(int x)
{
	check[x] = 1;
	for (int i = 1; i <= n; i++)
	{
		if (!check[i] && map[x][i])
			dfs(i);
	}
}


결과

문제출처: https://www.acmicpc.net/problem/11403


풀이

플로이드 와샬 알고리즘은 매우 직관적이고 쉬운 알고리즘으로써, 이 문제에 적용할 수 있습니다.

직관적으로 생각해보면, a=b, b=c 일때 a=c라는 삼단논법의 논리를 이용하자면,

출발하는길->거치는길이 있고, 거치는길에서 도착하는 길이 있으면, 출발하는길->도착하는길이 존재할 수 있습니다. 따라서 이 논리를 플로이드와샬 구현에 그대로 대입하면됩니다.


코드

#include<iostream>
using namespace std;
int map[100][100];
int n;
int main()
{
	cin >> n;
	for (int i = 0; i < n; i++)
		for (int j = 0; j < n; j++)
			cin >> map[i][j];
	//i=거치는길,j=출발,k=도착
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < n; j++)
		{
			for (int k = 0; k < n; k++)
			{
				//출발->거치는길이 있고, 거치는길->도착하는길이있으면
				if (map[j][i] && map[i][k])
					//출발->도착하는길이있다.
					map[j][k] = 1;
			}
		}
	}
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < n; j++)
		{
			cout << map[i][j] << " ";
		}cout << endl;
	}
}


결과

'문제풀이(BOJ) > 플로이드와샬' 카테고리의 다른 글

[백준 1956] 운동  (0) 2020.02.11
[백준 1389] 케빈 베이컨의 6단계 법칙  (0) 2020.02.05
[백준 2458] 키 순서  (1) 2020.01.24
[백준 10159] 저울  (0) 2020.01.24

문제출처: https://www.acmicpc.net/problem/1012


DFS를 이용한 풀이입니다. BFS를이용한 풀이는 링크를 참조해주세요. https://jow1025.tistory.com/87


풀이

BFS풀이법에서 언급했듯이 배추의 요소의 갯수만 알면되므로 dfs연산을 돌리는 횟수만 알면됩니다. 


코드

#include<iostream>
#include<cstring>
using namespace std;
int map[50][50];
int check[50][50];
int dx[4] = { 0,0,-1,1 };
int dy[4] = { -1,1,0,0 };
int m, n, k;
//가로, 세로,배추위치갯수
void dfs(int y, int x);
int main()
{
	int t;
	cin >> t;
	int x, y;
	while (t--)
	{
		memset(map, 0, sizeof(map));
		memset(check, 0, sizeof(check));
		cin >> m >> n >> k;
		int ans = 0;
		for (int i = 0; i < k; i++)
		{
			//좌표조심
			cin >> x >> y;
			map[y][x] = 1;
		}
		for (int i = 0; i < n; i++)
		{
			for (int j = 0; j < m; j++)
			{
				if (map[i][j] == 1 && check[i][j] == 0)
				{
					dfs(i, j);
					ans++;
				}
			}
		}
		cout << ans << endl;
	}
}
void dfs(int y, int x)
{
	check[y][x] = 1;
	for (int i = 0; i < 4; i++)
	{
		int nx = x + dx[i];
		int ny = y + dy[i];
		if (nx >= 0 & ny >= 0 && nx < m && ny < n)
		{
			if (map[ny][nx] == 1 && check[ny][nx] == 0)
			{
				check[ny][nx] = 1;
				dfs(ny, nx);
			}
		}
	}
}


결과



+ Recent posts