본문 바로가기

Algorithm

시뮬레이션 문제

시뮬레이션 문제란?


알고리즘을 풀 때 모든 과정이 제시되어 그 과정을 거쳐 나온 결과를 추론하는 문제입니다.

시뮬레이션은 설명해 준 대로 쭉 이행하면 됩니다.


코드에서 간단하게 설명드리도록 하겠습니다.


문제

( i 번째 기준 )

병의 용량 : capacities[i];

주스의 양 : bottles[i];

주스를 붓는 양 : toId[i];

주스를 붓고 남은 양 : fromId[i];


문제는 빈 병에다가 주스를 재분배해서 담으려하는데 0부터 M-1번째까지 M회 조작합니다.

i 번째 조작에 있어서는 fromId[i]부터 toId[i]에 주스를 담는 것을 의미합니다.

병 fromId[i]가 비어 있거나 병 toId[i]가 꽉 차 있다면 더이상 주스를 분배할 수 없습니다.



이러한 문제는 시키는 대로 순서대로 하면 되는데, 우선 이해해봅시다.

i번째 병에서 주스를 다른 병에 분배할 때 

fromId[i] 만큼 부으면 그 만큼 toId[i]에 채워지게 됩니다.


즉, bottles[fromId[i]] 만큼 부으면 bottles[toId[i]]는 그 만큼 채워지기 마련입니다.

단, bottles[toId[i]]는 bottles[fromId[i]] 만큼 채워지되, capacities[i] 보다 작아야 합니다. 




for(int i=0;i<fromId.length;i++){

int f = fromId[i]; // f 만큼 붓는 것

int t = toId[i]; // t 만큼 채워지는 것

int space = capacities[t] - bottles[t]; // t만큼 채워질 때 그 때의 병 용량 비는것과 채워지는 양만큼 계산했을 때 더 담을 수 있는 공간을 의미함.


if(space >= bottles[f]) {  // f 만큼 부을 때의 주스 양 >> 이것 보다는 담을 수 있는 공간이 더 커야함(같아도 됨)

int vol = bottles[f]; // f 만큼 부을 때의 주스 양

bottles[t] += vol;    // 부은 만큼 채워져야함

bottles[f] = 0;        // 가득 찰 경우 (붓는 만큼의 양을 딱 맞춰서 채워질 경우)

} else {

int vol = space;     // 여유 공간이 더 작기 때문에 여유 공간 만큼만 채워야함

bottles[t] += vol;   // 부은 만큼 채워져야함

bottles[f] -= vol;    // 여유 공간만큼 부었기 때문에 남을 것임 부어야 할 만큼에서 부은 만큼 빼서 남을 것을 계산

}

}



이런 식으로 정리할 수 있습니다.


추가로 조금 더 심화하자면 조건문으로 나눈 기준이 결국 여유 공간을 가지고 계산한 것이기 때문에 이를 수학적 사고를 가지고 변형할 수 있습니다.

i 번째의 병에서 주스를 부을 양과 담을 양을 계산해서 더해주면 결국 부어야할 전체의 양이 나오게 됩니다.

주스를 부어야 할 전체의 양에서 전체 병의 용량과 비교했을 때, 더 작은 값을 가지고 부어질 양에서 빼주면 됩니다.



public static int[] pour_the_juice(int[] capacities, int[] bottles, int[] fromId, int[] toId) {

/*

* 부을 수 있는 양 : fromId[i]; 붓는 양 : toId[i]; 전체 주스 양 : bottles[i]; 담겨질 수 있는 병의 용량 :

* capacities[i];

*/


/*

* 구조 1. 주스에서 붓는 양 만큼 해서 부을 경우 가득 채워질 때 (전체 주스를 부을 양보다 병의 용량이 더 클 경우) 

* 구조 2. 주스에서 붓는 양 만큼 해서 부을 경우 다 담지 못할 때 (전체 주스를 부을 양보다 병의 용량이 더 작을 경우)

*/


for (int i = 0; i < fromId.length; i++) {

// 붓는 양과 부을 수 있는 양의 합계는 전체 부어야 할 주스의 양과 같음

int sum = bottles[fromId[i]] + bottles[toId[i]];

// 전체 부어야 할 주스의 양과 담겨질 수 있는 병의 용량을 비교한다.

// 비교할 때 구조1로 들어가는 지 구조2로 들어가는 지 확인한다. 이를 조건문으로 나눌 수 있지만

// 결국 전체 부어야 할 주스의 양과 담겨질 수 있는 전체 병의 용량의 최소값을 찾는다면 더 편하다.

// 만일 부어야 할 주스의 양이 더 작은 값이라면 다음에는 부을 값이 없기 때문에 붓는 양은 0이 될 것이다.

// 반대로 병의 용량이 더 작은 값이라면 병의 용량을 가득 채워서 부어도 부어야할 주스가 남을 것이다. 이것이 붓는양의 값이 될 것이다.

bottles[toId[i]] = Math.min(sum,capacities[i]);

bottles[fromId[i]] = sum - bottles[toId[i]];  

}

return bottles;

}





다음과 같이 진행하게 되면 같은 값을 같게 될 것입니다.


이렇듯, 시뮬레이션 문제는 과정을 천천히 접근해서 풀면 됩니다.


다음에는 탐색 문제를 풀어보도록 하겠습니다. 

감사합니다.



반응형

'Algorithm' 카테고리의 다른 글

회문 작성 알고리즘  (0) 2018.05.01
전체 탐색 알고리즘 소개  (0) 2018.04.30
Collection  (0) 2018.01.08
그래프 탐색 알고리즘  (0) 2018.01.03
Dynamic Programming  (0) 2018.01.02