Programming

[BOJ] 16967 배열 복원하기

minigb 2021. 5. 14. 13:07

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

 

16967번: 배열 복원하기

크기가 H × W인 배열 A와 두 정수 X와 Y가 있을 때, 크기가 (H + X) × (W + Y)인 배열 B는 배열 A와 배열 A를 아래로 X칸, 오른쪽으로 Y칸 이동시킨 배열을 겹쳐 만들 수 있다. 수가 겹쳐지면 수가 합쳐

www.acmicpc.net

21.05.14 기준 문제 난이도 실버3

난이도는 실버3인게 맞는데

학회 슬랙에 올라온 질문 덕분에 고민을 많이 했다..

애초에 그 질문 덕분에 문제를 풀게 된 것이긴 하지만

 

우선 내 풀이는

배열 b를 이용해 배열 a의 내용을 최대한 구하고, 값이 구해지지 않은 부분은 또 구해주는 방법인데

int main()
{
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	int h, w, x, y; cin >> h >> w >> x >> y;
	vector<vector<int>> b(h + x, vector<int>(w + y));
	for (int i = 0; i < h + x; i++) {
		for (int j = 0; j < w + y; j++) {
			cin >> b[i][j];
		}
	}

	vector<vector<int>> a(h, vector<int>(w, -1));
	for (int i = 0; i < h; i++) {
		for (int j = 0; j < w; j++) {
			if (!((x <= i && i < h) && (y <= j && j < w))) {
				a[i][j] = b[i][j];
			}
			if (!((0 <= i && i < h - x) && (0 <= j && j < w - y))) {
				a[i][j] = b[i + x][j + y];
			}
		}
	}

	for (int i = 0; i < h; i++) {
		for (int j = 0; j < w; j++) {
			if (a[i][j] == -1) {
				a[i][j] = b[i][j] - a[i - x][j - y];
			}
		}
	}

	for (int i = 0; i < h; i++) {
		for (int j = 0; j < w; j++) {
			cout << a[i][j] << ' ';
		}
		cout << ENDL;
	}

	return 0;
}

조금 돌아갔다. 굳이 이렇게 할 필요가 없다.

슬랙에 질문이 올라온 코드를 보니 훨씬 풀이가 깔끔했다

 

 

각설하고

그 질문을 보면

 

16967번 배열 복원하기 문제입니다. 위 코드로 먼저 틀렸습니다를 받고 ans 배열을 사용 하지 않고도 풀 수 있겠다 싶어 아래 방식으로 코드를 조금 수정하여 제출 하자 올바른 풀이가 되었습니다. 두 코드의 차이는 새로운 정답 배열을 사용 하는지 여부 제외하곤 동일하다 생각하는데.. 왜 맞게 된 걸까요..

//틀렸습니다 받은 코드
#include<iostream>
using namespace std;
#define endl '\n'

int h,w,x,y;
int arr[601][601];
int ans[301][301];//*****
int main(){
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	
	cin>>h>>w>>x>>y;
	
	for(int i=0;i<h+x;i++){
		for(int j=0;j<w+y;j++){
			cin>>arr[i][j];
		}
	}
	
	for(int i=0;i<h;i++){
		for(int j=0;j<w;j++){
			if(i<x||j<y) ans[i][j]=arr[i][j];//********
			else ans[i][j]=arr[i][j]-arr[i-x][j-y];//*******
		}
	}
	
	for(int i=0;i<h;i++){
		for(int j=0;j<w;j++){
			cout<<ans[i][j]<<" ";//******
		}
		cout<<endl;
	}
	
	return 0;
}
//맞았습니다!! 받은 코드
#include<iostream>
using namespace std;
#define endl '\n'

int h,w,x,y;
int arr[601][601];

int main(){
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	
	cin>>h>>w>>x>>y;
	
	for(int i=0;i<h+x;i++){
		for(int j=0;j<w+y;j++){
			cin>>arr[i][j];
		}
	}
	
	for(int i=0;i<h;i++){
		for(int j=0;j<w;j++){
			if(!(i<x||j<y)) arr[i][j]-=arr[i-x][j-y];
		}
	}
	
	for(int i=0;i<h;i++){
		for(int j=0;j<w;j++){
			cout<<arr[i][j]<<" ";
		}
		cout<<endl;
	}
	
	return 0;
}

와... 진짜 고민을 많이 했다.. 둘이 뭐가 다를까..

그리고 깨달아서 다음처럼 답변했는데

 

 

반례를.. 찾았습니다..

3 3 1 1
1 1 1 0
1 3 2 1
1 2 3 1
0 1 1 1

1 1 1
1 2 1
1 1 1

틀린 코드에서의 출력

1 1 1
1 2 1
1 1 0

23번째 줄

else ans[i][j]=arr[i][j]-arr[i-x][j-y];

이 부분이

else ans[i][j]=arr[i][j]-ans[i-x][j-y];

가 되어야 합니다.
반례와 같은 상황에서 arr[i-x][j-y]도 겹쳐진 부분의 값일 수가 있습니다.
풀이의 흐름을 보면 b 배열의 겹쳐진 부분에서 a 배열의 값을 빼야 하는 것이기 때문에 arr의 값이 아닌 ans의 값을 빼야 하는 것이라고도 볼 수도 있습니다.

두 번째 코드와 다른 점이 없다고 생각했는데,
두 번째 코드는 arr가 ans와 동일하게 업데이트 되고, 첫 번째 코드에서는 그렇지 않다는 게 차이네요...

혹시 더 궁금한 점 있으시면 스레드에 또 질문해주세요!

 

 

아..

이걸 발견하는 데 정말 시간 많이 걸렸다.

발견 하고 나니까 왜 몰랐을까 싶다..