restore 문제 오답 케이스 조언 부탁드리겠습니다.

  • zzerross
    zzerross

    RESTORE

    문제 1주일째 풀고 있네요.

    주로 검산가능한 작은 데이터 위주로
    random data로 200개 정도 검토해 보고,
    coner case 수정했는데,

    계속 오답으로 해매고 있습니다.

    물론 최대 최소 긴 데이터들도 테스트해보긴 했구요.

    최대한 스스로 풀어보려고 했건만, 몇일동안 못풀고 있어 질문 드립니다.
    (iteration DP로 느리진 않을꺼라 생각했는데, 많이 느리기까지 하네요..)

    코드 로직은 아래와 같구요.

    • cmp(): 문자열 y 뒤에 x가 올때, 겹칠 수 있는 길이를 저장해서 cp[][]에 저장
    • slv(): 모든 문자열 조합의 결과가 되는 문자열의 최소 길이를 dp table에 작성
    • trk(): dp table에 작성된 최소 조합을 tracking하며 최소 문자열 출력

    소스코드는 아래와 같습니다.

    #include <stdio.h>
    
    #define ss 15
    #define sc 41
    #define sr (ss * sc)
    #define sf (1 << ss)
    #define nf ((1 << ns) - 1)
    
    int tc, ns, at[sc][sc];
    
    char as[ss][sc];
    
    struct cp {
        int l, x;
    } cp[ss][ss];
    
    struct dp {
        int l, t;
    } dp[sf][ss];
    
    struct q {
        int b[ss], r;
    } q;
    
    void read() {
        scanf("%d", &ns);
    
        for (int i = 0; i < ns; i++)
            scanf("%s", as[i]);
    }
    
    void cmp(int y, int x) {
        if (y == x)
            return;
    
        int i, j, k;
    
        for (i = 0; as[y][i]; i++) {
            for (j = 0; as[x][j]; j++) {
                at[i][j] = as[y][i] == as[x][j];
    
                if (i && j)
                    at[i][j] += at[i-1][j-1];
            }
        }
    
        cp[y][x].l = 0;
        cp[y][x].x = -1;
    
        cp[y][y].l = i;
        cp[x][x].l = j;
    
        for (k = j-1; k >= 0; k--) {
            if (k == at[i-1][k] - 1) {
                cp[y][x].l = at[i-1][k];
                cp[y][x].x = k;
    
                break;
            }
        }
    
        for (k = i-1; k >= 0; k--) {
            if (j == at[k][j-1]) {
                cp[y][x].l = j;
                cp[y][x].x = j;
    
                break;
            }
        }
    }
    
    void cmp() {
        for (int y = 0; y < ns; y++)
            for (int x = 0; x < ns; x++)
                cmp(y, x);
    }
    
    int slv() {
        for (int i = 0; i < ns; i++)
            dp[0][i].l = sr;
    
        for (int f = 1, l, b, t; f <= nf; f++) {
            for (int x = 0; x < ns; x++) {
                if (!(f & (1 << x)))
                    continue;
    
                dp[f][x].t = -1;
    
                if (b = (f - (1 << x))) {
                    dp[f][x].l = sr;
                } else {
                    dp[f][x].l = cp[x][x].l;
    
                    continue;
                }
    
                for (int y = 0; y < ns; y++) {
                    if (!(b & (1 << y)))
                        continue;
    
                    t = cp[y][y].l == cp[dp[b][y].t][y].l ? dp[b][y].t : y;
    
                    l = dp[b][y].l + cp[x][x].l - cp[t][x].l;
    
                    if (dp[f][x].l > l) {
                        dp[f][x].l = l;
                        dp[f][x].t = y;
                    }
    
                    if (dp[f][x].t == -1)
                        dp[f][x].t = y;
                }
            }
        }
    
        int m = 0;
    
        for (int i = 1; i < ns; i++)
            if (dp[nf][m].l > dp[nf][i].l)
                m = i;
    
        return m;
    }
    
    void trk(int i) {
        q.r = -1;
    
        for (int f = nf, t; i != -1; i = t) {
            q.b[++q.r] = i;
    
            t = dp[f][i].t;
    
            f -= (1 << i);
        }
    
        printf("%s", as[q.b[i = q.r]]);
    
        for (int y, x = 0, j, c = 1; --i >= 0;) {
            if (c)
                y = q.b[i+1];
    
            x = q.b[i];
    
            if (c = (cp[y][x].x != cp[y][x].l)) {
                j = cp[y][x].x + 1;
    
                printf("%s", &as[x][j]);
            }
        }
    
        printf("\n");
    }
    
    int main() {
        for (scanf("%d", &tc); tc--;) {
            read();
    
            cmp();
    
            trk(slv());
        }
    
        return 0;
    }
    


    10년 전
4개의 댓글이 있습니다.
  • Being
    Being

    전체적으로 코드의 의도를 파악하기가 쉽지가 않습니다. 가장 중요한 "dp table" 부분에 대한 설명이 미흡한데다가, 코드는 다른 사람이 이해할 수 있도록 작성되어 있지 않아서 읽기 어렵습니다.

    직전 단어와 그 전 단어만 참고하는 것으로 보이는 등 방법에 어느 정도 문제가 있는 것 같은데요, 아래 경우를 테스트해 보시기 바랍니다.

    4
    a
    b
    abc
    cd
    


    10년 전 link
  • zzerross
    zzerross

    답변 감사합니다.

    제시해 주신 케이스가 제대로 동작하지 않더군요.
    그래서 수정했는데, (abcd 맞겠죠?)
    여전히 오답이네요.

    먼가 다른 오답케이스가 또 있나보네요. ^^;
    케이스를 좀 더 테스트 해보도록 해야 겠네요...

    책의 코드 복기/STL 없이 푸는게 도통 쉽지 않네요...


    10년 전 link
  • zzerross
    zzerross

    지적해주신 코드의 가독성 부분...저도 인정하는 바이구요. ㅜ.ㅜ
    허접하게나마 dp table 부분을 설명해보면 다음과 같습니다.

    _as:
    [0] a
    [1] b
    [2] abc
    [3] cd
    
    _cp:
            0     1     2     3
     0:  1, 0  0,-1  1, 0  0,-1
     1:  0,-1  1, 0  0,-1  0,-1
     2:  1, 1  1, 1  3, 0  1, 0
     3:  0,-1  0,-1  0,-1  2, 0
    
    _dp: m=3
              :     0     1     2     3
    0x 0: 0000:   ,     ,     ,     ,
    0x 1: 1000:  1,-1   ,     ,     ,
    0x 2: 0100:   ,    1,-1   ,     ,
    0x 3: 1100:  2, 1  2, 0   ,     ,
    0x 4: 0010:   ,     ,    3,-1   ,
    0x 5: 1010:  3, 2   ,    3, 0   ,
    0x 6: 0110:   ,    3, 2  4, 1   ,
    0x 7: 1110:  3, 2  3, 2  4, 0   ,
    0x 8: 0001:   ,     ,     ,    2,-1
    0x 9: 1001:  3, 3   ,     ,    3, 0
    0x a: 0101:   ,    3, 3   ,    3, 1
    0x b: 1101:  4, 1  4, 0   ,    4, 0
    0x c: 0011:   ,     ,    5, 3  4, 2 <- 가로 축의  문자열을 
    0x d: 1011:  5, 2   ,    5, 0  4, 2    마지막으로 포함한
    0x e: 0111:   ,    5, 2  6, 1  4, 2    최소 길이,   문자열
    0x f: 1111:  5, 2  5, 2  6, 0  4, 2 <- 최소 문자열 조합
    
    q: 3 2 0 <-  dp table 0xf 최소값에서 부터 따라 올라감
    
    abcd <-  q 역순으로 중복되는 부분을 생략하여 출력
    


    10년 전 link
  • zzerross
    zzerross

    겨우 오답 케이스 찾아서 정답 처리됐네요.
    조언해 주신 Being 님 감사합니다.

    지적해주신 케이스와 유사하게 여러 형태로 여러번 중첩되는 경우가 계속 문제였네요.


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