AVOID 문제 코딩 실수 질문드립니다.

  • sven
    sven

    AVOID
    문제에서 코딩 실수가 있는 것 같은데, 찾지 못하겠어서 질문드립니다.
    플로이드 알고리즘으로 D[i][j][k]에 각 최단 경로를 구했고,
    이후 시작노드부터 최단경로로 소트하여 확률을 채워나갑니다.
    E 클래스는 간선을 의미하고, F 클래스는 분수의 표현을 의미합니다.
    저지에 제출하여 분모가 0이 아니라고 assert하는 부분에서 assertion failure가 나타납니다.
    제가 만들어본 예제들에서는 나타나지 않는 문제인데, 어떻게 해결하면 좋을까요?
    아래는 코드입니다.

    #include <iostream>
    #include <algorithm>
    #include <vector>
    #include <cassert>
    
    using namespace std;
    //class representing fractional numbers
    class F
    {
    public:
        int numer;
        int denom;
        F(int a, int b)
        {
            numer = a;
            denom = b;
            yakbun();
        }
    
        static F myAdd(F A, F B)
        {
            return F(A.numer * B.denom + A.denom * B.numer, A.denom * B.denom);
        }
    
        void myAdd(F A)
        {
    /* 
            numer = numer * A.denom + denom * A.numer;
            denom *= A.denom;
            yakbun();
     */
            *this = F::myAdd(A, *this);
        }
    
        void yakbun()
        {
            int i = 2;
            while(i*i <= numer && i*i <= denom)
            {
                while(numer %i == 0 && denom %i == 0)
                {
                    numer /= i;
                    denom /= i;
                }
                i++;
            }
        }
    
        F myDiv(int a)
        {
            return F(numer, denom * a);
            //return F(this->numer, this->denom * a);
        }
    
    };
    //class representing edge
    class E
    {
    public:
        int from, to, val;
        E(int a, int b, int c)
        {
            from = a;
            to = b;
            val = c;
        }
        bool friend operator < (E A, E B)
        {
            return A.val < B.val;
        }
    
    };
    
    void solve();
    
    int main()
    {
        int T;
        cin >> T;
        while(T--)
            solve();
    }
    
    void solve()
    {
        int N, M, targetSize;
        vector <int> target;
        vector <vector <E> > A;
        vector <vector <int> > B;
    
        cin >> N >> M >> targetSize;
        A.resize(N);
        B.resize(N);
    
        for(int i=0; i<M; i++)
        {
            int temp1, temp2, temp3;
            cin >> temp1 >> temp2 >> temp3;
            A[temp1-1].push_back(E(temp1-1, temp2-1, temp3));
            A[temp2-1].push_back(E(temp2-1, temp1-1, temp3));
        }
        for(int i=0; i<targetSize; i++)
        {
            int temp;
            cin >> temp;
            target.push_back(temp-1);
        }
    
    //floyd algorithm
        int D[N][N][N+1];
    
        for(int i=0; i<N; i++)
            for(int j=0; j<N; j++)
                for(int k=0; k<=N; k++)
                    D[i][j][k] = -1;
    
        for(int i=0; i<N; i++)
            for(int j=0; j<A[i].size(); j++)
                D[A[i][j].from][A[i][j].to][0] = A[i][j].val;
    
        for(int k=1; k<=N; k++)
            for(int i=0; i<N; i++)
                for(int j=0; j<N; j++)
                {
                    if(D[i][k-1][k-1] != -1 && D[k-1][j][k-1] != -1)
                        D[i][j][k] = D[i][k-1][k-1] + D[k-1][j][k-1];
                    if(D[i][j][k-1] != -1 && D[i][j][k] != -1)
                        D[i][j][k] = min(D[i][j][k], D[i][j][k-1]);
                    else if(D[i][j][k-1] != -1)
                        D[i][j][k] = D[i][j][k-1];
                }
    
        for(int i=0; i<N; i++)
            D[i][i][N] = 0;
    
    //sort by distance from the starting vertex, and calculate probabillity in each vertex
        vector <E> G;
        for(int i=0; i<N; i++)
            G.push_back(E(i,i,D[0][i][N]));
        sort(G.begin(), G.end());
        vector <F> FR;
        FR.push_back(F(1,1));
        for(int i=1; i<N; i++)
            FR.push_back(F(0,1));
    
        for(int i=0; i<N; i++)
        {
            int current = G[i].from;
            //cout << i << "th time, spreading the node " << current << endl;
            if(D[0][current][N] + D[current][N-1][N] != D[0][N-1][N])
                continue;
            vector <int> spread;
            spread.clear();
            for(int j=0; j<A[current].size(); j++)
                if(D[current][A[current][j].to][N] + D[A[current][j].to][N-1][N] == D[current][N-1][N])
                    spread.push_back(A[current][j].to);
            //cout << "it has value " << FR[current].numer << "/" << FR[current].denom << " and gonna divide it into " << spread.size() << " pieces" << endl;
    
            for(int j=0; j<spread.size(); j++)
    //            cout << "spreading to the node : " << spread[j] << endl;
                FR[spread[j]].myAdd(FR[current].myDiv((int)spread.size()));
        }
    
        for(int i=0; i<targetSize; i++)
            cout << FR[target[i]].numer << "/" << FR[target[i]].denom << endl;
    }
    

    11년 전
7개의 댓글이 있습니다.
  • JongMan
    JongMan

    코드를 좀 보려고 했는데 코드가 짧지 않아 어렵네요. 랜덤 데이터를 한 100개쯤 만들어서 돌려 보시는건 어떨까요?


    11년 전 link
  • sven
    sven

    코드 조금 간결하게 수정하였습니다.
    랜덤 데이터를 만들어 해결하는 방법은 대회에서 쓰기 힘들 것 같고 너무 의존하게 될 것 같아서 지양하고 있었는데, 일단 그렇게라도 해봐야겠습니다.


    11년 전 link
  • JongMan
    JongMan

    아.. 지금 깨달았는데

    정점의 수가 많아서 int로는 분모가 오버플로우할 수 있을 것 같습니다.


    11년 전 link
  • sven
    sven

    오, 실제로 그렇군요. 답변 감사합니다!


    11년 전 link
  • JongMan
    JongMan

    사실 엄밀히 데이터를 만들자면 64비트로도 모자랄 수 있는데요.. 정점의 수가 100개나 되어놔서.. 일단 이 데이터에선 괜찮습니다. ㅠ.ㅠ 나중에 N을 적절히 줄여야 하는데 건드리질 못했네요.


    11년 전 link
  • sven
    sven

    그런 경우에는 새로 변수 타입을 정의하여 사용해야겠죠?


    11년 전 link
  • JongMan
    JongMan

    데이터가 크면 그렇겠죠. 아니면 파이썬처럼 자체적으로 지원하는 언어를 쓰거나 ;)


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