Problem G 풀이 (C++)

 문제 링크 : https://codeforces.com/contest/1538/problem/G

이 문제를 푸는데 1주일이 넘게 걸렸던 것 같다.

결국 선형 계획법의 Simplex 방법을 이용해서 풀게 되었다.

( 해당 개념은 구글링을 통해 확인할 수 있다. )


x = red candy 개수

y = blue candy 개수

a = gift set 하나를 만드는데 필요한 red candy or blue candy 개수

b = gift set 하나를 만드는데 필요한 red candy or blue candy 개수


즉,

red candy a 개, blue candy b 개를 담거나

red candy b 개, blue candy a 개를 담아서 2가지 방법으로 gift 하나를 만들 수 있다.

red candy 인지 blue candy 인지는 중요하지 않기 때문에 x < y, a < b가 되도록 값을 설정한다.


여기서 선형 계획법을 쓰기 전에

x / a <= y / b인 경우에는 무조건 x / a 값이 최대 개수이다.

왜냐하면 보통 세트를 최대한 만들려고 할 때 큰 개수에서 큰 개수를 담는다.

y에서 최대한 b씩 담고 x에서 최대한 a씩 담은 후 세트 1개씩 담는 개수를 맞바꾼다면 바꿀 때마다 y는 +(b-a)가 될 거고 x는 -(b-a)가 된다.

맞바꾸다가 세트가 만들어질 수 있는 만큼 개수가 생긴다면 세트를 추가하는 방식으로 세트를 만들면 최대 개수를 만들 수 있게 된다. 

하지만 x / a <= y / b인 경우는 최대한 세트를 만들고 남은 x의 개수는 y의 개수보다 적기 때문에 최대 개수는 x / a 일 수밖에 없다.


[ 선형 계획법 Simplex method 적용 ]

y에서 a 만큼 담고 x에서 b 만큼 담는 방법으로 만든 gift set의 개수를 c,

y에서 b 만큼 담고 x에서 a 만큼 담는 방법으로 만든 gift set의 개수를 d라고 할 때

c + d = f의 최댓값을 구해야 한다.

조건식은

ac + bd <= y

bc + ad <= x

로 설정한다.

ac + bd <= x가 아닌 위의 조건식으로 설정하는 이유는 Simplex 방법을 실행할 때 어떤 입력값이든 축이 되는 행을 정하는 과정에서 항상 같은 방식으로 아래 행을 먼저 기준 행으로 잡고 다음에 위의 행을 기준 행으로 잡기 위함이다.


여유 변수까지 설정하여 정렬하면

ac + bd + u            = y

bc + ad +    + v      = x

-c  -d               + f = 0

이 된다.


Simplex 방법을 사용해 계산을 해보면

u와 v가 0일 때 f가 최대인 것을 알 수 있고

c = ( b*x - a*y ) / ( b*b - a*a )

d = ( b*y - a*x ) / ( b*b - a*a )

이 된다.

x, y, a, b를 대입하면 c와 d의 값을 구할 수 있다.

하지만 이렇게 계산할 경우 test case중 틀리는 case가 생긴다.

정확한 이유는 모르겠지만 여러가지 케이스를 대입해 본 결과,

c와 d의 값이 나누어 떨어지지 않고 소수값이 나오는 경우에 1set가 더 만들어지는 경우가 생겼다.

그래서 구한 c와 d에 버림 계산을 적용하여

x -= (bc + ad)

y -= (ac + bd)

해당 수량만큼 x와 y에서 빼고 나머지 x, y에서 세트가 만들어질 수 있는지 한번 더 확인하여 답을 구했다.


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
#include <iostream>
#include <algorithm>
 
using namespace std;
 
int main() {
 
    int t;
    cin >> t;
 
    while (t--) {
 
        long long x, y, a, b;
        cin >> x >> y >> a >> b;
 
        if (x > y)
            swap(x, y);
        if (a > b)
            swap(a, b);
 
        if (a == b) {
            cout << min(x / a, y / a) << endl;
            continue;
        }
 
        if (x / a <= y / b) {
            cout << x / a << endl;
            continue;
        }
 
        long long c = (((long double)b * x - a * y) / (b * b - a * a));
        long long d = (((long double)b * y - a * x) / (b * b - a * a));
 
        x -= (b * c + a * d);
        y -= (a * c + b * d);
 
        long long answer = c + d;
 
        if (min(x / a, y / b) > 0)
            answer += min(x / a, y / b);
        else if (min(x / b, y / a) > 0)
            answer += min(x / b, y / a);
 
        cout << answer << endl;
    }
 
    return 0;
}
cs

댓글