본문 바로가기
카테고리 없음

[C/C++] 6064: 카잉 달력

by soeayun 2023. 8. 24.

6064:카잉 달력 문제

 

 일단 이 이중 반복문을 통하여 모든 m*n케이스를 찾아보려고 할 경우 제 시간안에 찾아볼 수 없다. 그렇기 때문에 특정 조건일 때만 돌아가도록 만들어줘야 한다.

 

 이 문제는 결국 x+M*a=y+N*b 라는 a와 b에 관한 부정방정식을 푸는 것인데 결국 이는 답이 되는 수를 t라고 한다면 t%M==x t%N==y가 되도록 만들어줘야한다. 그렇기 때문에 while문에 들어가는 초기 변수가 j=x_target이 된 것이고 while문을 한번 돌때마다 x만큼 증가시켜주면서 만족하는 수가 있는지 확인해줘야한다.

 

 한가지 유의해야할 점은 x==x_target && y==y_target일 때인데 사실 이 경우 x%x_target이 x가 아니라 0이므로 이때를 주의하여 코드를 작성해야한다. 또한 이 경우에는 gcd 유클리드 알고리즘을 이용하여 최소공배수를 구하고 답을 구했다.

 

아래는 소스코드이다!

#include <stdio.h>
#include <math.h>

int gcd(int x,int y)
{
  if(y==0)
    return x;
  else
    return gcd(y,x%y);
  
      
}

int main(void)
  {
    int n,x,y,x_target,y_target;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
      {
        scanf("%d %d %d %d",&x,&y,&x_target,&y_target);
        if(x==x_target)
          x_target=0;
        if(y==y_target)
          y_target=0;
        int result=-1;
        int j=x_target;
        while(j<=x*y)
          {
            if(j%y==y_target)
            {
                          
              result=j;
              if(x_target==0 && y_target==0)
              {
                //printf("enter!\n");
                if(y>x)
                {
                  int tmp=y;
                  y=x;
                  x=tmp;
                }
                int tmp=gcd(x,y);
                //printf("gcd: %d\n",tmp);
                result=x*y/gcd(x,y);
              }
              break;
              
            }
            j+=x;
          }
        
        printf("%d\n",result);
      }
    
  }

 

 다른 풀이 방식으로는 처음에 M과 N의 최소공배수를 구하여 최소공배수를 넘어가지 않는 부분에서만 확인하는 것이다. (이렇게 하면 코드가 조금 더 짧고 break를 잘못쓸 수 있는 경우를 배제한다). 그 후

while(x <= limit) {
      if (y%N == x%N)
        break;
      x += M;
    }

를 이용하여 x를 N으로 나눈 나머지가 y를 N으로 나눈 나머지와 같을 때 결국 N이 부정방정식을 성립하게 된다. 이렇게 풀이할 경우 x==x_target && y==y_t일때 예외처리를 안해줘도 된다는 장점이 있다.

 

 또 x가 n으로 나누어 떨어질때를 방지하기 위할때 쓰는 방법이 있는데

x%n <=> (x-1)%n+1

i) x<n -> x%n는 x, (x-1)%n은 x-1이라서 +1해주면 x

ii) x=a*n -> x%n는 a*n%n이라서 0, (x-1)%n은 (a*n-1)%n은 ((a-1)*n+n-1)%n으로 n-1이라서 +1해주면 n

iii) x>n -> x%n은 (a*n+b)%n이라서 b, (x-1)%n은 (a*n+b-1)%n은 b-1이라서 +1해주면 b

따라서, % 연산에 대해 a%b == (a+c)%b-c 가 성립한다는 것을 알 수 있다.

 

그렇기 때문에 이 문제에서도 

(j-1)%y+1==y_target

를 이용하면 더 간단히 풀 수 있다. 위에서 말한 두가지 방법을 합해 소스코드를 고쳐보면 아래와 같이 된다!

#include <stdio.h>
#include <math.h>

int gcd(int x,int y)
{
  if(y==0)
    return x;
  return gcd(y,x%y);      
}

int main(void)
  {
    int n,x,y,x_target,y_target;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
      {
        scanf("%d %d %d %d",&x,&y,&x_target,&y_target);
        int result=-1;
        int max=x*y/gcd(x,y);
        int j=x_target;
        while(j<=max)
          {
            if((j-1)%y+1==y_target) // % 연산에 대해 a%b == (a+c)%b-c 가 성립한다 (c는 상수)
            {                          
              result=j;
            }
            j+=x;
          }
        
        printf("%d\n",result);
      }
    
  }

 

카잉 달력에 관한 반례와 FAQ이며 여기서 나온 테스트케이스를 가지고 코드를 확인하면 된다.

https://www.acmicpc.net/board/view/21503