[editorial] ACM-ICPC 2008 Seoul Regional Problem G - Guess

  • User Avatar
    wookayin

    시험기간에 공부안하고 쉬운문제 에디토리얼 쓰는 욱 그 두번째 글입니다

    이번 문제는 총19개 팀이 풀어 YES 수로는 4위에 랭크된 문제로 성공률은 25.33%였습니다. 참고로 3위(C)는 20팀, 5위(H)는 18팀이 풀었으니 비교적 쉬운 문제였다고 볼 수 있죠.
    잡담을 좀 넣자면 대회 때 저희 팀은 이 문제를 상당히 늦게 잡았었는데, 조금 더 일찍 시도해 보았더라면 좋았을텐데[...] 어려운 건줄 알고..... 그래요.
    문제 설명
    문제는 간단합니다. 어떤 미지의 정수 수열 a[1], ..., a[n]이 있는데 이 수열의 원소값은 알지 못하고
    가능한 모든 구간 (i, j)에 대하여 {a[i]+a[i+1]+...+a[j]} 의 부호(+, 0, -)만을 알려줍니다.
    그렇다면 가능한 수열 a[] 를 추론해 내는 것이 문제입니다. 원소의 개수는 10 이하이고, 원소값 또한 -10 이상 10 이하.
    풀이
    이 문제는 간단한 백트래킹으로 해결할 수 있습니다. 흔히 말하는 '다돌리기'.
    가장 단순한 방법은 1010 가지의 가능한 경우를 다 해보는 겁니다. -10 이상 10 이하이지만, 원소 하나만의 부호는 알 수 있기 때문에 각 원소의 절대값을 1부터 10까지만 해보면 되겠죠.
    1010 이라면 시간이 빡빡하긴 한데, 사실 웬만한 인풋에 대해서 구간의 합 sign 을 만족하는 수열은 꽤 많습니다.
    어차피 하나만 찾으면 되기 때문에, 실제로 각 원소마다 10개를 다 해보지 않고 어느 몇개만 해도 답이 보통 다 나오더군요-_-a
    그리고 도중에 조건을 만족하지 않는 경우에서는 자동으로 가지치기가 되니까, 생각보다 빠른 시간 안에 답을 구할 수 있습니다.
    그래서 실제 대회 때는 이렇게 '다 해보기' 를 해서 많은 팀들이 YES를 받을 수 있었습니다.
    소스 코드
    ~~~ cpp

    #include
    int a[11]; // backtracking 하면서 결정한 수열을 저장
    int s[11]; // s[i] = sum{ a[k] }, 1<=k<=i
    int sign[11][11];
    int n;
    char str[1024];
    #define sum(x, y) (s[y] - s[x-1]) //sum(x, y) = sum{ a[k] }, x<=k<=y
    bool go(int d)
    {
    // 완성
    if(d > n) return true;
    // 0인 경우는 바로 다음으로 넘어감
    if(sign[d][d] == 0)
    {
    a[d] = 0; s[d] = s[d - 1];
    return go(d+1);
    }
    for(int i=1; i<=10; ++i)
    {
    a[d] = sign[d][d] * i;
    s[d] = s[d-1] + a[d];
    // i 를 시도해 본 후, 가능한가 검사
    bool able = true;
    for(int j=1; j<=d && able; ++j)
    {
    if(sign[j][d] ==0 && sum(j, d) != 0) able = false;
    if(sign[j][d] < 0 && sum(j, d) >= 0) able = false;
    if(sign[j][d] > 0 && sum(j, d) <= 0) able = false;
    }
    if(able && go(d+1))
    return true;
    }
    return false;
    }
    int main()
    {
    int T;
    scanf("%d", &T);
    while(T-- > 0)
    {
    scanf("%d %s", &n, str);
    int g = 0;
    // 입력 루틴
    for(int i=1; i<=n; ++i) for(int j=i; j<=n; ++j)
    {
    if(str[g] == '0') sign[i][j] = 0;
    else if(str[g] == '+') sign[i][j] = 1;
    else sign[i][j] = -1;
    g++;
    }
    // backtracking
    if( go(1) )
    {
    for(int i=1; i<=n; ++i)
    printf("%d ", a[i]);
    }
    putchar('\n');
    }
    return 0;
    }

    
    
    <div>[이 글은 과거 홈페이지에서 이전된 글입니다. <a href="http://algospot.andromeda-express.com/zbxe/editorial/53958">원문보기</a>]</div>
    

    17년 전
0개의 댓글이 있습니다.
  • 정회원 권한이 있어야 커멘트를 다실 수 있습니다. 정회원이 되시려면 온라인 저지에서 5문제 이상을 푸시고, 가입 후 7일 이상이 지나셔야 합니다. 현재 문제를 푸셨습니다.