문제 : https://www.acmicpc.net/problem/2981


[수학지식]

수학적인 지식이 정말 많이 쓰인 문제입니다.

차근차근 정리해보도록 하겠습니다.

 

K개의 자연수가 있다고 하자. ( 1 ≤ k ≤ 100 )

이때, M으로 나눈 나머지가 t로 모두 같다고 하면 다음과 같은 식이 성립한다.

[정리1]

이 식을 이용해 다음과 같은 정리를 얻을 수 있습니다.

 

[정리2]

임의의 i, j 에 대해

[정리3]

즉, 두 개의 숫자의 차이는 M으로 나눠짐을 알 수 있습니다.

[최종정리]

M으로 모든 숫자들을 나누었을 때, 나머지가 같다면 정리 2,3에 의해 M은 임의의 두 수의 차이의 약수가 됩니다.

즉, M은 두 숫자의 차이 값들의 공약수가 됩니다.

따라서, 모든 숫자들의 차이에 대해서 최대공약수를 구하고,  최대공약수의 약수를 출력해주면 되는 문제입니다.

 

[+추가정리]

수학과 정수론 시간에 배우는 정리로, 증명은 너무 간단하니 생략하겠습니다.

위의 정리를 이용하면 다음과 같은 연산 낭비를 막을 수 있습니다.

예를 들어 N1, N2, N3, N4 ( N1 < N2 < N3 < N4 ) 수가 주어졌을 때, 가능한 모든 차이는

N4 - N3 / N4 - N2 / N4 - N1 / N3 - N2 / N3 - N1 / N2 - N1 이 됩니다.

그리고 우리는 이 6개의 값에 대해서 GCD(최대공약수)를 구해야 됩니다.

하지만 위의 정리를 이용하면, 

N4 - N3 / N3 - N2 / N2 - N1 의 최대공약수만 구해도 됩니다.

 

[알고리즘풀이]

1. 입력된 숫자들을 크기에 따라 정렬해준다.

2. 입력된 숫자들의 모든 차이에 대해서 최대 공약수(g)를 구해준다.

3. 그 최대공약수의 약수(g)들을 출력해준다.

#include<iostream>
#include<algorithm>
#include<vector>

using namespace std;

int gcd(int a, int b) {
	if (b == 0)
		return a;
	else
		return gcd(b, a % b);
}

int main(void) {
	ios::sync_with_stdio(false);
	cin.tie(0);

	int n;
	cin >> n;
	vector<int> v;
	for (int i = 0; i < n; i++) {
		int temp;
		cin >> temp;
		v.push_back(temp);
	}
	sort(v.begin(), v.end());

	int g = gcd(v[1] - v[0], v[1] - v[0]);

	for (int i = 1; i < n - 1; i++) {
		g = gcd(g, v[i + 1] - v[i]);
	}
	vector<int> vv;

	for (int i = 1; i * i <= g; i++) {
		if (g % i == 0) {
			if (i == 1) vv.push_back(g);
			else if (i * i == g)
				vv.push_back(i);
			else{
				vv.push_back(i);
				vv.push_back(g / i);
			}
		}
	}

	sort(vv.begin(),vv.end());
	for (int i = 0; i < vv.size(); i++)
		cout << vv[i] << ' ';
}

 

'Problem Solving > BOJ' 카테고리의 다른 글

[BOJ] 14888 - 연산자 끼워넣기  (0) 2019.09.21
[BOJ] 2580 - 스도쿠  (0) 2019.09.21
[BOJ] 1182 - 부분수열의 합  (0) 2019.09.20
[BOJ] 1788 - 피보나치 수의 확장  (0) 2019.09.20
[BOJ] 17298 - 오큰수  (0) 2019.09.20

+ Recent posts