BOJ 2494 숫자 맞추기


사용한 알고리즘

  • DP, DFS

알고리즘 설명

  • 먼저 나사의 성질을 확실하게 알아야한다.
  • 나사를 왼쪽으로 돌리면 아래에 있는 나사들도 같이 돌아간다.
  • 오른쪽으로 돌리면 돌리는 나사만 돌아간다.
  • 이는 위쪽에 있는 나사에 따라서 아래쪽 나사들이 연관이 있다는 의미이다.
  • 문제 상황을 나누면, 1. 위에서 몇번 왼쪽으로 돌렸는지, 2. 몇번째 나사인지 이 두가지로 나눌 수 있다.
  • 1번째 상황은 0~9번으로 표현할 수 있다.(10번돌리나 20번돌리나 같은 값)
  • 2번째 상황은 총 10000개 이하이다.
  • 총 필요한 문제 상황은 10만개로 메모이제이션(memoization)으로 표현할 수 있다.
  • DP(i,j): 위에서 i번 왼쪽으로 돌렸을때, j번째~N번째 나사를 모두 맞추기 위해서 돌려야하는 최소 횟수
  • 문제 DP를 정의하고 왼쪽으로 돌리는 경우, 오른쪽으로 돌리는 경우 2가지로 나누어 DFS를 진행한다.
  • 왼쪽으로 돌려야하는 횟수 = l, 오른쪽으로 돌려야하는 횟수 r
  • 점화식: DP(i,j) = min(DP(i+1, j+l)+l, DP(i+1,j)+r)
  • 이후에 다시 얼마나 돌려야 하는지는 DP를 이용해서 추적을 하게 된다.
  • 구한 점화식으로 각 나사에서 오른쪽으로 돌려 맞춰야 하는지 왼쪽으로 돌려서 맞춰야 하는지를 판단한다.
  • 결과를 출력한다.

풀이

// baekjoon 2494 yechan
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAX_N=10002;

int N, dp[10][MAX_N];
char S[MAX_N], D[MAX_N];

// here screw and remain turns to left
// 위에서부터 현재 나사 위치 here, 지금까지 왼쪽으로 돌린 횟수 turns
int dfs(int here, int turns) {
// 모든 나사를 맞춘경우
	if (here == N) return dp[turns][here]=0;

	int &ret = dp[turns][here];
	if (ret != -1) return ret;
	ret = 1e9; // init

// 나사를 맞추기 위해서 왼쪽으로 돌려야 하는 횟수
	int df = (20+((int)(D[here]-'0')-(int)(S[here]-'0'))-turns)%10; // remain to left
// 왼쪽으로 돌리는 경우
	ret = min(ret, dfs(here + 1, (df+turns)%10) + df);
// 오른쪽으로 돌리는 경우
	ret = min(ret, dfs(here + 1, turns) + (10-df)%10);
	return ret;
}

void backTracking(int here, int turns) {
	if (here == N) return;

	int df = (20+((int)(D[here]-'0')-(int)(S[here]-'0'))-turns)%10; // remain to left
// 오른쪽으로 돌리는 경우
	if (dp[turns][here+1] != -1 && dp[turns][here]-dp[turns][here+1] == (10-df)%10) {
		printf("%d %d\n", here+1, -(10-df)%10);
		backTracking(here+1, turns);
	}
// 왼쪽으로 돌리는 경우
	else {
		printf("%d %d\n", here+1, df);
		backTracking(here+1, (10+df+turns)%10);
	}
}

int main() {
	memset(dp, -1, sizeof(dp));
	scanf("%d", &N);
	scanf("%s%s", S, D);
	printf("%d\n", dfs(0, 0));
	backTracking(0, 0);
	return 0;
}



© 2020.08. by fbdp1202

Powered by theorydb