[BOJ] 1248 : 맞춰봐

1248 : 맞춰봐

풀이

백트래킹

숫자 n개가 주어질때 0,-,+ 부호를 보고 각숫자가 어떤 숫자가 될 수 있는지 맞히는 문제이다.

각 부호는 행렬의 우삼각행렬에서 부분합을 뜻한다.

0 1 2 3 4

1 * x y z

2 - * y z

3 - - * z

4 - - - *

이때 첫번째 포인트는 i==j인곳(*표시)는 A[i]가 된다는 점이다.

그것만 가지고 가능한 경우를 모두 탐색하고, x==n이 되었을때 종료시키면 시간초과가 뜬다.

그래서 두번째 포인트!

마지막에 올바른지 판단하지 않고 각 숫자를 넣어봤을때 바로바로 중간점검을 할 수있게 바꿨다.

즉 x가3일때(3번째 x를 탐색할떄) 탐색에 앞서서 ans[0]과 ans[1]의 합의 부호가

위 그림의 ‘x’가 맞는지 살폈다.

그리고 x가4일때 탐색에 앞서서 위 그림의 ‘y’들이 올바른 부호인지 살폈다.

코드

#include <iostream>
#include <vector>
#include <cstring>
using namespace std;

char arr[10][10];
vector<int> ans;
int n;
int flag = 0;
void go(int x) {
    if (flag)
        return;
    //전에 넣은숫자를 중간점검
    int sum = 0;
    for (int j = 0; j < x; j++) {
        sum += ans[j];
    }
    for (int j = 0; j < x-1; j++) {
        if (arr[j][x-1] == '0') {
            if (sum != 0)
                return;
        }
        else if (arr[j][x - 1] == '+') {
            if (sum <= 0)
                return;
        }
        else if (arr[j][x - 1] == '-') {
            if (sum >= 0)
                return;
        }
        sum -= ans[j];
    }

    if (x == n) {
        //정답찾음
        flag = 1;
        for (int x : ans) {
            cout << x << ' ';
        }
        return;
    }

    if (arr[x][x] == '0') {
        ans.push_back(0);
        go(x + 1);
        ans.pop_back();
    }
    else if (arr[x][x] == '+') {
        for (int i = 1; i <= 10; i++) {
            ans.push_back(i);
            go(x + 1);
            ans.pop_back();
        }
    }
    else {
        for (int i = -10; i <= -1; i++) {
            ans.push_back(i);
            go(x + 1);
            ans.pop_back();
        }
    }
}

int main() {
    cin >> n;
    char trash;
    scanf("%1c", &trash);
    for (int i = 0; i < n; i++) {
        for (int j = i; j < n; j++) {
            scanf("%1c", &arr[i][j]);
        }
    }
    go(0);
    return 0;
}
Written on April 8, 2019