문제 출처:https://www.acmicpc.net/problem/11404



 문제


n(1≤n≤100)개의 도시가 있다. 그리고 한 도시에서 출발하여 다른 도시에 도착하는 m(1≤m≤100,000)개의 버스가 있다. 각 버

스는 한 번 사용할 때 필요한 비용이 있다.

모든 도시의 쌍 (A, B)에 대해서 도시 A에서 B로 가는데 필요한 비용의 최소값을 구하는 프로그램을 작성하시오.


입력


첫째 줄에 도시의 개수 n(1≤n≤100)이 주어지고 둘째 줄에는 버스의 개수 m(1≤m≤100,000)이 주어진다. 그리고 셋째 줄부  

터 m+2줄까지 다음과 같은 버스의 정보가 주어진다. 먼저 처음에는 그 버스의 출발 도시의 번호가 주어진다. 버스의 정보는 

버스의 시작 도시 a, 도착 도시 b, 한 번 타는데 필요한 비용 c로 이루어져 있다. 시작 도시와 도착 도시가 같은 경우는 없다. 비

용은 100,000보다 작거나 같은 자연수이다.


 출력


N개의 줄을 출력해야 한다. i번째 줄에 출력하는 j번째 숫자는 도시 i에서 j로 가는데 필요한 최소 비용이다. 만약, i에서 j로 갈 수 없는 경우에는 그 자리에 0을 출력한다.


이 문제는 모든 정점의 쌍(A,B)에 대해서 A에서 B로 가는데 필요한 비용의 최소값을 구하는 문제입니다.

따라서 그래프의 가능한 모든 노드 쌍에 대해 최단 거리를 구하는 알고리즘인 플로이드-워셜 알고리즘을 사용한다면 쉽게 풀수 있습니다.

플로이드 워셜 알고리즘의 시간 복잡도는 O(n³)인데 문제의 n은 최대 100 이므로 제한 시간인 1초 이내에 충분히 풀 수 있다는것을 알 수 있습니다. 


이 문제에선 추가적으로 처리해줘야 할 부분이 두가지 있는데 그중 첫번째는 중복되는 노드의 값이 들어온다는 것입니다.

출처로 들어가 문제의 예제 입력부분을 확인하면 1번에서 4번 노드로 가는 값이 2번 중복되는 것을 볼 수 있습니다.

따라서 중복되는 노드값이 입력될때 비용이 더 적은 값으로 바꿔주어야 합니다.

 for (int i = 0; i < m; i++) {

int a, b, c;

scanf("%d %d %d", &a, &b, &c);

if (adj[a - 1][b - 1] > c)

adj[a - 1][b - 1] = c;

}

저는 입력부분에 위 코드를 추가해줌으로써 첫번째 문제를 해결하였습니다.


두번째 문제는 출처에 들어가 출력부분을 읽어보면 "만약, i에서 j로 갈 수 없는 경우에는 그 자리에 0을 출력한다." 라는 말이 있습니다.

저는 서로 갈 수 없는 노드는 INF로 처리했기 때문에 플로이드 워셜 알고리즘을 수행한 후에 출력부분에 INF를 0으로 바꿔주는 코드를 추가했습니다. 

for (int i = 0; i < n; i++) {

for (int j = 0; j < n; j++) {

if (adj[i][j] == INF)

adj[i][j] = 0;

printf("%d ", adj[i][j]);

}

printf("\n");

}

밑줄친 부분이 추가해준 코드입니다.


플로이드 워셜 알고리즘이란 - https://ko.wikipedia.org/wiki/%ED%94%8C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EC%9B%8C%EC%85%9C_%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98


전체 코드입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#pragma warning(disable:4996)
#include <iostream>
#include <cstdlib>
#define INF 100000000
 
 
using namespace std;
 
int main() {
    int n, m;
    scanf("%d %d"&n, &m);
    int **adj = new int*[n];
 
    for (int i = 0; i < n; i++)
        adj[i] = new int[n];
 
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            adj[i][j] = (i == j) ? 0 : INF;
        }
    }
 
    for (int i = 0; i < m; i++) {
        int a, b, c;
        scanf("%d %d %d"&a, &b, &c);
        if (adj[a - 1][b - 1> c)
            adj[a - 1][b - 1= c;
    }
 
    for (int k = 0; k < n; k++) {
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                adj[i][j] = (adj[i][j] < (adj[i][k] + adj[k][j])) ? adj[i][j] : adj[i][k] + adj[k][j];
            }
        }
    }
 
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            if (adj[i][j] == INF)
                adj[i][j] = 0;
            printf("%d ", adj[i][j]);
        }
        printf("\n");
    }
 
    return 0;
}
cs

문제 출처:https://www.acmicpc.net/problem/1629



 문제


자연수 A를 B번 곱한 수를 알고 싶다. 단 구하려는 수가 매우 커질 수 있으므로 이를 C로 나눈 나머지를 구하는 프로그램을 작성하시오.


 입력


첫째 줄에 A, B, C가 빈 칸을 사이에 두고 순서대로 주어진다. A, B, C는 모두 2,147,483,647 이하의 자연수이다.


 출력


첫째 줄에 A를 B번 곱한 수를 C로 나눈 나머지를 출력한다.


이 문제는 얼핏보면 그냥 A의 제곱을 구하는 간단한 문제로 보입니다. 그러나 막상 문제를 풀어보면 2가지 문제점을 발견하게 됩니다. 


1. 숫자의 범위가 자료형의 표현 범위보다 커진다.

2. O(n)의 시간복잡도로 문제를 풀어도 시간제한에 걸린다.  


1번의 해결방법으론 long long 자료형을 사용하는 것입니다.

그러나 이러한 자료형을 사용하더라도 문제는 제곱수이기 때문에 적은 연산에도 숫자가 기하급수적으로 커지게 되고 결과적으로 long long자료형의 범위도 넘어가버리게 됩니다.

따라서 연산을 할때마다 C로 나눈 나머지로 값을 바꿔주어야 자료형의 범위를 넘지 않고 계산이 가능합니다.


2번은 B의 크기가 최대 2,147,483,647인데 이 수를 for문을 사용하여 그대로 2,147,483,647번 반복한다면 대략 1억번의 반복을 1초라 생각했을때 21초나 걸리게 되어 문제의 시간제한인 2초를 훨씬 넘기게 되버립니다.

따라서 이 문제는 for문을 사용하지 않고 함수의 재귀를 이용하는 분할정복 알고리즘을 이용하여 풀어야 O(log n)의 시간복잡도를 가지게 되고 문제의 최대값이 들어와도 바로 결과값을 나타낼수 있습니다.


분할정복 알고리즘이란 https://ko.wikipedia.org/wiki/%EB%B6%84%ED%95%A0_%EC%A0%95%EB%B3%B5_%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <stdio.h>
#include <stdlib.h>
 
long long int a, b, c;
int result = 0;
long long int pow(long long int x, long long int m) {
 
    if (m == 0)
        return 1;
    else if (m == 1)
        return x;
    if (m % 2 > 0)
        return pow(x, m - 1)*x;
    long long int half = pow(x, m / 2);
    half %= c;
    return (half * half) % c;
}
 
int main() {
 
    scanf("%lld %lld %lld"&a, &b, &c);
 
    printf("%lld", pow(a, b) % c);
 
    return 0;
}
cs


+ Recent posts