Koder / 박성훈
article thumbnail

분할정복 컷

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

 

2339번: 석판 자르기

하나 이상의 불순물과 보석 결정체로 이루어진 석판을 여러 조각으로 나누어 가공해서, 보다 높은 가치를 얻을 수 있도록 만들려고 한다. 이때, 높은 가치의 석판을 만들기 위해서는 석판을 여

www.acmicpc.net

보석 결정이 자르는 선 위에 위치하면 안된다는 조건을 놓쳐서

고민좀함.

선을 하나 그어주면 가로선인 경우로 예시를 들 때

선 윗부분의 직사각형 하나 ( 다음에 세로선을 그어야함 )

선 아랫부분의 직사각형 하나 ( 다음에 세로선을 그어야함 )

그래서 그어야할 선 방향을 매개변수로 하나 더 넣어주는 재귀함수를 짜면

분할정복이 됨.

 

모든 점에 대해서 가로세로로 선 그어서 쪼개는경우 다 생각해줘도

TLE는 안받았다.

 

재귀 탈출조건 생각하기가 좀 비직관적이라 느꼈는데

불순물이 없는 부분 직사각형은 분할정복의 가장 끝 노드? 에 해당하니까

반드시 보석 결정체가 "하나만" 있어야한다.

 

이게 아닌 경우는 불순물이 있으니까 쪼갤수 있을것.

쪼갠 뒤에 보석이 없던지말던지 그건 다음 재귀함수에서 알아서 빠져나올것이다.

 

더보기
#include <bits/stdc++.h>
#define x first
#define y second
using namespace std;

int arr[23][23] = {0};

int recur(int x1, int y1, int x2, int y2, bool dir){
	
	//printf("[%d %d] to [%d %d]\n", x1,y1,x2,y2);
	
	int jewel = 0;
	int trash = 0;
	int ret = 0;
	
	vector<pair<int,int>> pos;
	
	for(int i=x1; i<=x2; i++){
		for(int j=y1; j<=y2; j++){
			if(arr[i][j] == 1){
				trash++;
				pos.push_back({i,j});
			}
			if(arr[i][j] == 2){
				jewel++;
			}
		}
	}
	
	if(trash == 0){
		if(jewel == 1) return 1;
		else           return 0;
	}
	
	for(auto k : pos){
		bool flag = true;
		if(dir){
			for(int i=y1; i<=y2; i++) if(arr[k.x][i] == 2) flag = false;
			if(flag) ret += recur(x1,y1,k.x-1,y2, !dir) * recur(k.x+1,y1,x2,y2, !dir);
		}
		else{
			for(int i=x1; i<=x2; i++) if(arr[i][k.y] == 2) flag = false;
			if(flag) ret += recur(x1,y1,x2,k.y-1, !dir) * recur(x1,k.y+1,x2,y2, !dir);
		}
	}
	return ret;
}

int main(){
	int n;
	cin >> n;
	
	for(int i=0; i<n; i++){
		for(int j=0; j<n; j++){
			cin >> arr[i][j];
		}
	}
	int ret = recur(0,0,n-1,n-1,0) + recur(0,0,n-1,n-1,1);
	if(ret == 0) cout << -1;
	else		 cout << ret;
	return 0;
}

코드가 좀 길긴 한데

그냥 간단히 설명해보자면 가로세로로 잘라줘야할 불순물 점을

vector<pair<int,int>> pos 에 저장하고

 

탈출조건 체크해준다음에

쪼개야할 경우에는 dir 매개변수 보고 방향 맞춰서 잘라줬다.

자를때 윗쪽/아래쪽 or 왼쪽/오른쪽은 서로 독립사건이니까 곱해주면 됨.

 

보석결정이 위로 올라가면 안되니까 bool flag 하나 만들고

자를 선 순회하면서 있는지 체크한번 해줬다.

반응형