https://school.programmers.co.kr/learn/courses/30/lessons/68936?language=cpp
프로그래머스
SW개발자를 위한 평가, 교육의 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프
programmers.co.kr
내 풀이
#include <string>
#include <vector>
#include <iostream>
using namespace std;
//dfs
/*
1. dfs(완탐) 방식으로 정사각형의 width와 height 를 4분할한다.(for문 조건문)
2. isOneSet: 4분할을 통해 0과 1 둘 중 하나가 Full로 되있는지 아닌지 체크한다.
3. Full로 되있으면 더 이상 해당 lv의 브랜치에서 4분할로 쪼갤 필요 없다.(continue)
Full로 되있으면 continue하기 전에 rt[0](0의 갯수), rt[1](1의 갯수) 중 하나에 +1 카운팅
4. Full이 아니면 재귀 이용해서 4분할로 쪼갠다.
5. 언제까지 재귀를 진행 할건지 : width 또는 height 반절까지의 lv
행렬의 width 또는 height가 1, 2, 4, 8 즉 2의 n승만큼 증가하는 것이기에
width 또는 height 의 반절까지의 lv에 접근해서 가지치기 한다.
해당 마지막 lv에 도착 할 때 2번(isOneSet)을 진행한다.
Full이 안되있으면 사용하는 CntZeroOne 구조체의 oneCnt와 zeroCnt의 누적갯수를
rt[0], rt[1]에 누적해 더한다.
*/
int rt[2]; // answer에 넣을 0, 1 값의 갯수 누적 배열
const int branch = 4; // 4분할이기에 branch는 4개
//행렬에서 0의 갯수, 1의 갯수 카운팅
// isUse: CntZeroOne 구조체 사용여부(카운팅 사용여부)
// 0또는 1이 리프 노드 이전에 full로 되있을 경우 CntZeroOne 구조체는 사용할 필요가 없다.
// isOneSet메서드를 2번 사용하는데
// 리프노드에서 한번, for문 recursion 들어가기 전에 한번 사용한다.
struct CntZeroOne{
int oneCnt;
int zeroCnt;
bool isUse;
};
//0 또는 1 Full여부체크, 0과 1 을 카운팅 할 시 CntZeroOne tmp 사용 (참조형태로 여러개의 갯수를 반환하게끔 진행)
int isOneSet(int stX, int stY, int enX, int enY,
const vector<vector<int>>& arr,
CntZeroOne& tmp )
{
bool isOne = false;
bool isZero = false;
for(int y = stY; y <= enY; ++y){
for(int x = stX; x <= enX ; ++x){
if(arr[y][x] == 1){
isOne = true;
if(tmp.isUse !=false){
tmp.oneCnt++;
}
}
else{
isZero = true;
if(tmp.isUse !=false){
tmp.zeroCnt++;
}
}
}
}
if(isOne && isZero) return -1;
if(isOne) return 1;
if(isZero) return 0;
return -1;
}
void recursion(int stL, int stH, int enL, int enH, int lv, const vector<vector<int>>& arr)
{
// 리프노드에서 더 이상 재귀돌리지 않게 가지치기
if(lv >= (arr.size()/2)){
CntZeroOne tmp = {0,0, true}; // Full 여부 체크 및 0또는 1 갯수 체크
int isRt = isOneSet(stL,stH,enL,enH,arr, tmp);
if(isRt == -1){
rt[0]+=tmp.zeroCnt;
rt[1]+=tmp.oneCnt;
}
else if(isRt == 0){ // 모두 0인 경우
rt[0] +=1;
}
else{ // 모두 1인 경우
rt[1]+=1;
}
return;
}
int rtTmp = -1; // Full 여부 체크 용도의 flag
for(int idx = 0 ; idx < branch ; ++idx){
CntZeroOne tmp = {0,0, false}; //단순 0또는 1의 full여부만체크하고자 3번째 인자는 false처리 (0또는 1 각각 카운팅 목적의 인자)
if(idx == 0){ // 정사각형의 상좌
rtTmp = isOneSet(stL, stH, (((stL +1) + (enL+1))/2) -1,(((stH + 1) + (enH + 1))/2) -1 ,arr , tmp);
if(rtTmp == 1){
rt[1]++;
continue;
}
else if(rtTmp == 0){
rt[0]++;
continue;
}
recursion(stL, stH, (((stL +1) + (enL+1))/2) -1,(((stH + 1) + (enH + 1))/2) -1 ,lv+1, arr);
}
if(idx == 1){ // 정사각형의 상우
rtTmp = isOneSet((((stL +1) + (enL+1))/2), stH, enL,(((stH + 1) + (enH + 1))/2) -1 ,arr , tmp);
if(rtTmp == 1){
rt[1]++;
continue;
}
else if(rtTmp == 0){
rt[0]++;
continue;
}
recursion((((stL +1) + (enL+1))/2), stH, enL,(((stH + 1) + (enH + 1))/2) -1 ,lv+1, arr);
}
if(idx == 2){ // 정사각형의 하좌
rtTmp =isOneSet(stL, (((stH + 1) + (enH + 1))/2), (((stL +1) + (enL+1))/2) -1,enH ,arr , tmp);
if(rtTmp == 1){
rt[1]++;
continue;
}
else if(rtTmp == 0){
rt[0]++;
continue;
}
recursion(stL, (((stH + 1) + (enH + 1))/2), (((stL +1) + (enL+1))/2) -1,enH ,lv+1, arr);
}
if(idx == 3){ // 정사각형의 하우
rtTmp = isOneSet((((stL +1) + (enL+1))/2), (((stH + 1) + (enH + 1))/2), enL, enH, arr , tmp);
if(rtTmp == 1){
rt[1]++;
continue;
}
else if(rtTmp == 0){
rt[0]++;
continue;
}
recursion((((stL +1) + (enL+1))/2), (((stH + 1) + (enH + 1))/2), enL, enH,lv+1, arr);
}
}
return;
}
int initFullCheck(vector<vector<int>>arr){
CntZeroOne tmp = {0,0,false};
int rt = isOneSet(0,0,arr.size()-1, arr.size()-1, arr, tmp); // 사각형 전체 범위 체크
if(rt == 1) return 1;
if(rt==0)return 0;
return -1;
}
vector<int> solution(vector<vector<int>> arr) {
vector<int> answer;
//테스트케이스 10번: 정사각형의 초기값이 0 또는 1로 Full 된 경우
int tmpRt = initFullCheck(arr);
if(tmpRt == 1){
answer.push_back(0);
answer.push_back(1);
}
else if(tmpRt == 0){
answer.push_back(0);
answer.push_back(1);
}
else{ // 그렇지 않을 경우 정사각형 4분할 쪼개는 재귀 진행
recursion(0,0,arr.size() - 1,arr.size() - 1, 0,arr);
answer.push_back(rt[0]);
answer.push_back(rt[1]);
}
return answer;
}
테스트케이스 10번 만 틀려서 힌트 얻고 initFullCheck 메서드 만들어서 진행.(처음에 모두 1 또는 0 인 경우)
dfs 방식으로 진행,
브랜치별 넓이와 높이를 4분할로 쪼갯다.
재귀를 돌리기 전 각 영역에 1 또는 0 이 해당 영역에 완전히 있는지 체크.
0과 1 둘다 같이 있으면 재귀를 계속 진행( arr 사이즈의 1/2 까지)
0과 1 중 하나라도 full로 있으면 더 이상 재귀를 돌리지 않고 rt[0] 또는 rt[1] 에 +1
dfs의 lv이 arr 사이즈의 1/2 까지 올 때 0과 1이 full 인 경우 또는 그렇지 않을 경우를 구분해서
full이면 해당 [0] 또는 [1] 인덱스에 +1
full이 아닐 경우 tmp.zeroCnt와 tmp.oneCnt 만큼 rt[0], [1] 에 각 누적해서 더함
TODO: 코드가 지저분하다, 타인 코드 참고하여 코드를 깔끔하게 만들기.
타인 코드
#include<string>
#include<vector>
using namespace std;
vector<vector<int>>MAP;
void DFS(int y, int x, int Size, vector<int>& answer) {
if (Size == 1) {
if (MAP[y][x] == 0) {
answer[0]++;
}
else answer[1]++;
return;
}
bool Zero, One;
Zero = One = true;
for (int dy = y; dy < y + Size; ++dy) {
for (int dx = x; dx < x + Size; ++dx) {
if (MAP[dy][dx] == 0) One = false;
if (MAP[dy][dx] == 1) Zero = false;
}
}
if (Zero) {
answer[0]++;
return;
}
if (One) {
answer[1]++;
return;
}
DFS(y, x, Size / 2, answer);
DFS(y, x + Size / 2, Size / 2, answer);
DFS(y + Size / 2, x, Size / 2, answer);
DFS(y + Size / 2, x + Size / 2, Size / 2, answer);
}
vector<int>solution(vector<vector<int>>arr) {
vector<int>answer(2, 0); // 1차원 벡터 2개 원소 0으로 초기화
MAP = arr;
DFS(0, 0, arr.size(), answer);
return answer;
}'프로그래머스 > 코딩테스트' 카테고리의 다른 글
| lv2) 퍼즐 게임 챌린지[] (0) | 2026.05.23 |
|---|---|
| lv2 ) 귤고르기 (0) | 2026.05.19 |
| SQL lv1) 특정 옵션이 포함된 자동차 리스트 구하기 (0) | 2026.05.03 |
| SQL lv2) 조건에 부합하는 중고거래 상태 조회하기 (0) | 2026.05.03 |
| SQL lv1 ) 한 해에 잡은 물고기 수 구하기 (0) | 2026.05.03 |
