ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 백준 11092(Safe Passage)
    전공/알고리즘 2020. 8. 3. 14:21

    https://www.acmicpc.net/problem/11092

     

    11092번: Safe Passage

    A group of friends snuck away from their school campus, but now they must return from the main campus gate to their dorm while remaining undetected by the many teachers who patrol the campus. Fortunately, they have an invisibility cloak, but it is only lar

    www.acmicpc.net

     

     

    투명망토를 쓰고 학교로 돌아가는데 한번에 최대 두명만 쓸 수 있고, 속도는 더 느린쪽에 맞춰서 이동한다. 

    그리고 모두 학교로 다시 돌아와야 되기 때문에 입구까지 투명망토를 전달할 한 명도 필요하다. 모두 학교로 다시 돌아갈 때까지의 최단 시간은?

     

    음 직관적인 문제라고 해야되나 간단한 비교를 통해서 찾을 수 있는 것이지만 찾기가 어려웠다. 

     

    먼저 생각할 수 있는건 느린놈들끼리 보내는게 시간을 줄이는데에 도움이 된다. 

     

    어차피 둘 중에 더 느린 사람의 속도에 맞추기 때문에 단순하게 이렇게 생각할 수 있다. 

     

    그렇다면 이제 학교로 가는 경우만 말고, 입구로 돌아가는 경우까지 생각해보자

     

    입구로 돌아갈때 가장 시간이 적게 걸리려면 학교에 있는 사람 중에서 가장 빠른 사람을 혼자 보내는 경우이다. 

     

    이 경우는 조금만 생각해봐도 무조건 가장 빠른 사람 보내는게 최종적으로 시간이 적게 걸린다는 걸 알 수가 있다. 

     

     

     

    문제는 학교에 있는 사람 중에서 가장 빠른 사람이 누가 되느냐이다. 

     

    학교에 있는 사람이 무조건 전체에서 가장 빠른 사람이면 좋겠지만 그렇지 않은 경우에 가장 최단시간인 경우가 있을 수 있기 때문이다. 

     

    예시를 들면 

     

    4

     

    1 2 7 10

     

    인 경우에 

     

    1 2 를 학교로 보내고

    1을 입구로 보내고 

    7 10 을 학교로 보내고 

    2를 입구로 보내고

    1 2를 학교로 보내고 

     

    이 경우가 최단 시간이 되는 경우인데, 4번째 줄에서 보이듯이 1이 아닌 2가 학교에 있는데도 최단시간이 만들어 졌다. 

     

    만약에 1을 계속해서 학교로 보냈다가 입구로 보냈다가를 반복하면 

     

    1 2를 학교로 보내고

    1을 입구로 보내고

    1 10을 학교로 보내고

    1을 입구로 보내고

    1 7을 학교로 보내고 

     

    21의 시간이 걸리게 되고, 최단 시간이 아니게 된다. 

     

    또 가장 느린 사람과 그 다음 느린 사람을 같이 보낸 경우가 아닌 경우

     

    1 3 6 8 12

     

    에서 보자 

     

    1 3 학교로 보내고 

    1 입구로 보내고 

    6 8 학교로 보내고 

    3 입구로 보내고 

    1 12 학교로 보내고 

    1 입구로 보내고 

    1 3 학교로 보내고 

     

    만약에 느린사람끼리 묶어두지 않으면 

     

    위와 같이 12 + 6 인 경우가 아닌 12+8인 경우가 발생하게 된다. 따라서 최고로 느린 사람과 그 다음 느린사람을 최대한 묶어서 학교로 보내야 한다. 

     

    위에 경우를 정리하면 

     

    전제조건은 가장 느린사람들끼리 최대한 묶어서 보낸다. 

     

    입구로 보낼애가 누군지 시간비교를 통해서 결정한다. 

    (반드시 가장 작은 두 명 중 한명이 된다.)

     

     

    증명은 없다 그냥 그런것같다. 

     

    그래서 나는 먼저 가장 빠른 둘을 학교로 보냈다. (맨 마지막에 보내더라도 위 전제조건 1에 위배되지 않는다. 그리고 입구로 보내야 하므로, 가장 빠른애가 학교에 있어야 한다.)

     

    그 뒤에 학교에 있는 애 중에서 가장 빠른애를 입구로 보냈다.

     

    이제 비교를 한다. 

     

    두 번째로 빠른 애를 입구로 이동시키는데에 이용할 것인지 가장 빠른애를 이용할 것인지

     

    만약에 4 100 101 102 103 이 있다고 하면 

     

    100 이 학교에 있고, 

     

    4 101 102 103이 입구에 있다. 

     

    학교로 가장 큰 두놈을 묶어서 보내보자 

     

    103 102 학교로 보낸다

    100 입구로 보낸다 (학교에서 있는 것 중에서 가장 빠른 애)

    4 100 학교로 보낸다. 

    4 입구로 보낸다.

     

    vs

     

    4 103 학교로 보낸다. (4가 가장 빠르므로 4를 학교로 보내야 입구로 보낼 때 4를 쓸 수 있다)

    4 입구로 보낸다.

    4 102 학교로 보낸다. 

    4 입구로 보낸다. 

     

    두 경우 모두 위의 상황에서 102와 103만 보낸 경우이다. 

    따라서 전 후 상황이 같으므로 이런식으로 비교해서 더 빠른 걸 선택해도 상관이 없다. 

     

    위 경우에는 밑의 경우가 더 빠르므로 밑에껄 선택해서 사용한다. 

     

    하나의 경우를 더 확인해보자

    4 100 1234 12345 123456 

     

    이 있는 경우를 확인해보자 

     

    4 100 학교

    4 입구

    에서 

     

    123456 12345 학교 

    100 입구

    4 100 학교 

    4 입구

    12345 4 학교 

     

    vs

     

    123456 4 학교

    4 입구

    12345 4 학교 

    4 입구 

    1234 4 학교

     

    위에 경우가 더 최적의 상황이다. 

     

    따라서 매번 비교를 통해서 가장 빠른 애를 이용할 것인지 그 다음 빠른것을 이용할 것인지 값 비교를 통해서 확인한다. 

     

    위의 상황을 모두 학교로 갈때까지 반복한다.

     

    #include<iostream>
    #include<algorithm>
    using namespace std;
    #define MAX 5001
    
    int N, num, sum, start1, start2;
    int speed[16];
    bool dorm[MAX];
    bool done;
    
    int main() {
    	cin >> N;
    	num = N;
    	for (int i = 0; i < N; i++) {
    		cin >> speed[i];
    	}
    	sort(speed, speed + N);
    	start1 = speed[0];
    	start2 = speed[1];
    	dorm[start2] = true;
    	num -= 2;
    	sum += start2;
    	while (num) {
    		sum += start1;
    		num += 1;
    		int n1 = -1, n2 = -1;
    		for (int i = 0; i < N; i++) {
    			if (dorm[speed[i]]) continue;
    			if (n1 < speed[i]) {
    				n2 = n1;
    				n1 = speed[i];
    			}
    		}
    		if (num == 2) {
    			sum += n1;
    			num -= 2;
    		}
    		else {
    			int left = n1 + n2 + start1;
    			int right = n1 + start2 * 2;
    			if (left < right) {
    				sum += left;
    			}
    			else {
    				sum += right;
    			}
    			dorm[n1] = true;
    			dorm[n2] = true;
    			num -= 3;
    		}
    	}
    	cout << sum << "\n";
    }

     

     

     

    '전공 > 알고리즘' 카테고리의 다른 글

    백준 10090(Counting Inversions)  (0) 2020.08.05
    백준 2812(크게 만들기)  (0) 2020.08.05
    백준 1817(짐 챙기는 숌)  (0) 2020.07.31
    백준 9576(책 나눠주기)  (0) 2020.07.31
    백준 19539(사과나무)  (1) 2020.07.30

    댓글

Designed by Tistory.