※ 글쓴이는 취미로 코딩을 익혀보는 사람이라 정확하지 않은 내용을 담고 있을 수 있다 ※

 

이번에 볼 문제는 백준 2647번 문제인 검은점과 하얀점 연결이다.
문제는 아래 링크를 확인하자.

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

 

2647번: 검은점과 하얀점 연결

2n개의 점이 x축의 좌표 1,2,...2n에 놓여 있다. 그 중 n개는 검은 점이고, n개는 하얀 점이다. 하나의 검은 점과 하나의 하얀 점을 연결하여 한 쌍을 만들면, 모두 n개의 쌍이 만들어진다. 한 쌍의 점

www.acmicpc.net

주어진 문자열에서 검은점과 흰점의 개수가 같은 부분구간에 대한 부분문제를 생각할 수 있다.

이를 이용해 식을 잘 세우면 DP를 통해 구간의 높이와 거리합을 저장해가면서 재귀적으로 전체 구간의 최소 거리합을 구할 수 있다.

 

출력 또한 위 과정에서 역추적할 구간들을 미리 기록해두는 것으로 재귀적으로 가능하다.

 

아래는 제출한 소스코드이다.

#include <iostream>
#include <string>
using namespace std;

int dp[101][101][6]; // dp[L][R][0/1] 0: L부터 R까지 최소거리, 1: 그때 높이 // 3~6: L1 R1 L2 R2
int prefixsum0[101];
int prefixsum1[101];

void func(int L, int R) {
	if (R == L + 1) {
		dp[L][R][0] = 3;
		dp[L][R][1] = 1;
		return;
	}

	int ret = 1000000007, ret_h, retL1, retR1, retL2, retR2;
	if (prefixsum0[R - 1] - prefixsum0[L] == prefixsum1[R - 1] - prefixsum1[L]) {
		if (dp[L + 1][R - 1][0] == 0) func(L + 1, R - 1);
		ret_h = dp[L + 1][R - 1][1] + 1;
		ret = dp[L + 1][R - 1][0] + 2 * ret_h + (R - L);
		retL1 = L, retR1 = R, retL2 = L + 1, retR2 = R - 1;
	}

	for (int r = L + 1; r < R; r += 2) {
		int l = r + 1;
		if (prefixsum0[r] - prefixsum0[L - 1] != prefixsum1[r] - prefixsum1[L - 1]) continue;

		if (dp[L][r][0] == 0) func(L, r);
		if (dp[l][R][0] == 0) func(l, R);
		int temp_h = max(dp[L][r][1], dp[l][R][1]);
		int temp = dp[L][r][0] + dp[l][R][0];
		if (ret > temp) {
			ret = temp, ret_h = temp_h, retL1 = L, retR1 = r, retL2 = l, retR2 = R;
		}
	}

	dp[L][R][0] = ret;
	dp[L][R][1] = ret_h;
	dp[L][R][2] = retL1;
	dp[L][R][3] = retR1;
	dp[L][R][4] = retL2;
	dp[L][R][5] = retR2;
}

void output(int L, int R) {
	if (R == L + 1) {
		cout << L << ' ' << R << '\n';
		return;
	}

	int L1 = dp[L][R][2], R1 = dp[L][R][3], L2 = dp[L][R][4], R2 = dp[L][R][5];

	if ((L1 == L && R1 == R)) {
		cout << L1 << ' ' << R1 << '\n';
		output(L2, R2);
	}
	else {
		output(L1, R1);
		output(L2, R2);
	}
}

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);

	int N; cin >> N;
	string s; cin >> s;
	for (int i = 0; i < N; i++) {
		if (s[i] == '0') {
			prefixsum0[i + 1] = prefixsum0[i] + 1;
			prefixsum1[i + 1] = prefixsum1[i];
		}
		else {
			prefixsum1[i + 1] = prefixsum1[i] + 1;
			prefixsum0[i + 1] = prefixsum0[i];
		}
	}

	func(1, N);

	cout << dp[1][N][0] << '\n';
	output(1, N);
}
728x90

'BOJ' 카테고리의 다른 글

[BOJ 2862 // C++] 수학 게임  (0) 2021.06.08
[BOJ 1068 // C++] 트리  (0) 2021.06.07
[BOJ 1213 // C++] 팰린드롬 만들기  (0) 2021.06.05
[BOJ 1043 // C++] 거짓말  (0) 2021.06.04
[BOJ 1208 // C++] 부분수열의 합 2  (0) 2021.06.03

+ Recent posts