메모리: 35308 KB, 시간: 744 ms
브루트포스 알고리즘, 구현
2024년 5월 27일 13:36:17
폴리오미노란 크기가 1×1인 정사각형을 여러 개 이어서 붙인 도형이며, 다음과 같은 조건을 만족해야 한다.
정사각형 4개를 이어 붙인 폴리오미노는 테트로미노라고 하며, 다음과 같은 5가지가 있다.
아름이는 크기가 N×M인 종이 위에 테트로미노 하나를 놓으려고 한다. 종이는 1×1 크기의 칸으로 나누어져 있으며, 각각의 칸에는 정수가 하나 쓰여 있다.
테트로미노 하나를 적절히 놓아서 테트로미노가 놓인 칸에 쓰여 있는 수들의 합을 최대로 하는 프로그램을 작성하시오.
테트로미노는 반드시 한 정사각형이 정확히 하나의 칸을 포함하도록 놓아야 하며, 회전이나 대칭을 시켜도 된다.
첫째 줄에 종이의 세로 크기 N과 가로 크기 M이 주어진다. (4 ≤ N, M ≤ 500)
둘째 줄부터 N개의 줄에 종이에 쓰여 있는 수가 주어진다. i번째 줄의 j번째 수는 위에서부터 i번째 칸, 왼쪽에서부터 j번째 칸에 쓰여 있는 수이다. 입력으로 주어지는 수는 1,000을 넘지 않는 자연수이다.
첫째 줄에 테트로미노가 놓인 칸에 쓰인 수들의 합의 최댓값을 출력한다.
문제의 조건을 다시 한번 보자면
"도형은 모두 연결되어 있어야 한다."
"정사각형 4개를 이어 붙인 폴리오미노"
"회전이나 대칭을 시켜도 된다."
정도가 중요한 키포인트일 것이다.
그래서 작동 방식을 이렇게 한번 짜봤다.
- 해당 장소를 방문했는 지 아닌 지 여부를 체크한다.
- 방문하지 않았다면 DFS를 실행한다.
- DFS에서, 동서남북 중 하나를 선택해 원하는 위치로 한칸 이동시킨다.
- 정사각형 개수 + 1을 하고 DFS를 실행시킨다.
- 정사각형이 4개가 될 경우 최댓값인지 확인한다.
-> 이를 바탕으로 좀 더 자세하게 알고리즘을 짜보자.
1. 방문 여부를 체크한다. 만약 방문하지 않았다면 DFS를 실행한다.
2. DFS에서는 위치와 정사각형 개수, 합계를 매개변수로 가진다.
3. 초기값은 각각위치좌표 (x,y), 정사각형 개수=1, 해당 위치 배열 값을 가진다.
4. DFS에선 동서남북으로 각각 이동시킨다.
5. 만약 배열 범위에 벗어날 경우, continue시킨다.
6. 새로운 위치가 방문하지 않는 위치라면 방문 여부를 true로 변경하고 새로운 위치에서 DFS를 실행한다.
7. 만약 정사각형 개수가 4개가 된다면 최댓값인지 체크한다.
8. 최댓값을 출력한다.
이를 구체화해서 코드를 짜보면
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class Main {
//DFS로 푼 문제
static int N,M;
static int map[][]; //배열
static boolean visited[][]; //방문 여부 확인용
static int maxValue=0; //계산할 최댓값
static int[] dx = {0,0,-1,1}; //동서남북 이동
static int[] dy = {-1,1,0,0}; //동서남북 이동
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st = new StringTokenizer(br.readLine());
N = Integer.parseInt(st.nextToken()); //입력받기
M = Integer.parseInt(st.nextToken());
map= new int[N+1][M+1]; //크기 설정
visited = new boolean[N+1][M+1];
for(int i=1; i<=N; i++) { //배열 입력
st = new StringTokenizer(br.readLine());
for(int j=1; j<=M; j++) {
map[i][j]=Integer.parseInt(st.nextToken());
}
}
for(int i=1; i<=N; i++) { //dfs 실행
for(int j=1; j<=M; j++) {
if(!visited[i][j]) {
visited[i][j]= true; //초기 위치 방문
dfs(j,i,1,map[i][j]);
visited[i][j]= false;
}
}
}
System.out.println(maxValue);
}
public static void dfs(int row,int col,int count,int sum) {
if(count==4) {//블럭이 4개가 채워지면 종료
maxValue = Math.max(maxValue, sum);
}
else {
for(int i=0; i<4; i++) {
int newRow = row + dx[i]; //새로운 이동 위치
int newCol = col + dy[i];
if(newCol <=0 || newRow <=0 || newCol > N || newRow > M) { //범위 벗어나면 X
continue;
}
if(!visited[newCol][newRow]) {
if(count==2) { //(ㅗ)자 모양 만들기 위한 용도
visited[newCol][newRow] = true;
dfs(row,col,count+1,sum+map[newCol][newRow]);
visited[newCol][newRow] = false;
}
visited[newCol][newRow] = true;
dfs(newRow,newCol,count+1,sum+map[newCol][newRow]);
visited[newCol][newRow] = false;
}
}
}
}
}
<주의>
주의해야 할 점은 정사각형 개수가 2개일 때 (ㅗ) 모양을 만들기 위해서는 DFS를 이동시킨 새로운 위치가 아닌 원래 위치에서 DFS를 실행시켜야 한다.//주의!! dfs(row,col,count+1,sum+map[newCol][newRow]);
[티스토리 - 테트로미노 참고] 위 블로그를 참고했다.
(ㅗ) 모양 부분을 해결하지 못했는데 도움이 됐다.