LAN 문제에 대해 질문 드립니다

  • wraithkim
    wraithkim

    LAN문제를 풀다가 두가지 걸림돌이 있어서 질문드립니다.
    지금 C++의 자료형과 문법을 약간 쓰고 있는데 C++ 지식 없습니다...
    (처음 질문해서 설명에 실수가 있을 수도 있습니다. 이해가 안되시면 물어봐주세요.)

    1. 시간 초과
      지금 시간초과로 계속 틀리고 있는데 제 생각에는 더 이상 연산 과정이 줄어드는 방법이 없다고 생각하는데
      이러면 알고리즘을 갈아 엎는게 맞는 건가요...

    2. 오차
      예제 출력을 하면 10-7 이상에서 오차가 생기는데, 원인을 모르겠습니다. 예상으론 제 루트 계산법이 문제이거나
      아니면 프림 알고리즘을 잘못 응용해서 최솟값이 아닌 값이 나오는 것 같은데 건물 사이의 거리가 정수가 아니라서
      계산할 엄두가.... 안 나네요...

    #include <stdio.h>
    #define SQR_LEN(x, y) ((x)*(x))+((y)*(y))
    
    float SQRT(float val)
    {
        float total = 1;
        for(int i = 0; i< 8; i++)
        {
            total = (total+(val/total))/2;
        }
    
        return total;
    }
    
    int main()
    {
        int num_case;
        float total_length[50];
    
    
        //준비 과정
        scanf("%d", &num_case);
        for(int i = 0; i < num_case; i++) //테스트 케이스(50번 이하) 루프
        {
            int num_build, num_cable_installed;
            int p_build[500][2]; //건물의 좌표
            int temp[3];
            char graph_build[500][500] = { 0, };
            //0: nonconnected c: connected a: already connected
            bool connected_build[500] = { 0, };
    
    
            //첫 줄은 건물 개수(500개 이하)와 설치된 케이블 개수(2000개 이하)
            scanf("%d %d", &num_build, &num_cable_installed);
    
            //건물들의 x좌표
            for(int j = 0; j < num_build; j++)
            {
                scanf("%d ", &p_build[j][0]);
            }
            //건물들의 y좌표(좌표는 전부 1000이하)
            for(int j = 0; j < num_build; j++)
            {
                scanf("%d ", &p_build[j][1]);
            }
            //설치된 케이블 하나당 연결된 건물...
            for(int j = 0; j < num_cable_installed; j++)
            {
                scanf("%d %d", &temp[0], &temp[1]);
                graph_build[temp[0]][temp[1]] = graph_build[temp[1]][temp[0]] = 'a';
            }
    
            //해결 과정
                //프림 알고리즘 응용
                //출발점 하나를 지정한다.
                //만약 지금 이어진 노드가 이미 이어진 케이블이 있으면 그 노드도 연결시킨다.
                //기본적으론 지나간 점들에 이어진 에지 중 최솟값을 다음 이어진 노드로 정한다.
    
                //출발점은 건물 0번
                //가장 큰 루프는 전체 건물이 케이블로 이어져 있을 때 탈출
    
                    //연결된 노드가 새로 생기면 루프는 지금 노드에서 이미 설치된 케이블이 있는지 확인한다.
                        //있으면 자동으로 트리에 연결한다.
                    //루프는 지금 지나간 모든 노드에서 가장 최소 에지를 찾는다
                        //단 시작노드와 끝노드가 전부 지나간 노드인 경우는 제외한다.(트리에 속해있으므로)
                    //새로 이어질 최소 에지는 총 설치해야할 케이블 길이에 추가한다.
    
            connected_build[0] = true; //출발점 지정
            total_length[i] = 0;
            while(true)
            {
                //연결된 노드 중 이미 설치된 케이블이 있는지 확인하는 루프
                while(true)
                {
                    bool is_end = true;
                    for(int j = 0; j < num_build; j++)
                    {
                        for(int k = j; k < num_build; k++)
                        {
                            if((connected_build[j] || connected_build[k]) && graph_build[j][k] == 'a')
                            {
                                graph_build[j][k] = 'c';
                                connected_build[k] = connected_build[j] = true;
                                is_end = false;
                                break;
                            } 
                        }
                    }
    
                    if(is_end)
                        break;
                }
    
                //모든 건물이 연결되어 있는가
                bool is_all_connect = true;
    
                for(int j = 0; j < num_build; j++)
                {
                    if(!connected_build[j])
                    {
                        is_all_connect = false;
                        break;
                    }
                }
    
                if(is_all_connect)
                    break;  //해결 루프 탈출
                else
                {
                    //최소 에지 찾기
    
                    temp[2] = 0; 
                    //temp: [0] = 최소 에지의 시작노드, [1] = 최소 에지의 끝노드, [2] = 최소 에지의 길이의 제곱
    
                    for(int j = 0; j < num_build; j++)
                    {//이어진 노드 찾기
                        for(int k = j; k < num_build; k++)
                        {
                            if(connected_build[j] || connected_build[k])
                            {
                                if(connected_build[j] && connected_build[k])   //이미 연결된 노드는 제외
                                    continue;
    
                                if(!temp[2])//temp값 초기화
                                {
                                    temp[0] = j;
                                    temp[1] = k;
                                    temp[2] = SQR_LEN(p_build[k][0] - p_build[j][0], p_build[k][1] - p_build[j][1]);
                                    continue;
                                }
    
                                if(temp[2] > SQR_LEN(p_build[k][0] - p_build[j][0], p_build[k][1] - p_build[j][1]))
                                {
                                    temp[0] = j;
                                    temp[1] = k;
                                    temp[2] = SQR_LEN(p_build[k][0] - p_build[j][0], p_build[k][1] - p_build[j][1]);
                                }
                            }
                        }
                    }
    
                    //최소 에지에 케이블 연결
                    graph_build[temp[0]][temp[1]] = 'c';
                    connected_build[temp[0]] = connected_build[temp[1]] = true;
                    total_length[i] += SQRT((float)temp[2]);
                }
            }
    
            //해결 과정 end
    
        }
    
        //출력: 각 케이스 당 설치할 케이블들의 최소 합
        for(int i = 0; i < num_case; i++)
        {
            printf("%.12f\n", total_length[i]);
        }
    
        return 0;
    }
    

    9년 전
1개의 댓글이 있습니다.
  • Kureyo
    Kureyo

    1)알고리즘 자체는 모르겠으나 프림구현부터 비효율적입니다..ㅠㅜ 최외각 while (true)안의 2중 루프들을 다 1중 루프이하로 줄일 수 있어보입니다
    2)math 라이브러리의 sqrt함수를 사용하시는게..


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