본문 바로가기

앳코더_AtCoder/Beginner Contest

[앳코더/Contest] Atcoder Beginner Contest 169

 

클릭하면 앳코더 페이지로 이동합니다.


 

#ABC 169 후기

 

처음 해보는 앳코더 대회인데, Beginner 난이도라 그런지 난이도는 A~C까지는 아이디어만 금방 떠올린다면 쉽게 풀 수 있었고,
D번부터는 점점 난이도가 많이 올라서 푸는데 조금 어려움이 있었다.
A~C가 비슷한 주제 (Multiplication)로 나와서 코드포스랑은 조금 다른점이 신기했다.
앞으로 코드포스랑 병행해서 자주 대회를 해봐야겠다고 생각했다.

 


 

#ABC 169 문제

  A번 Multiplication 1 - 성공

더보기

 

A x B를 출력하시오.

 

A와 B의 범위가 1~100의 자연수이므로 그냥 A * B를 계산해서 출력하면 되는 문제였다.

 

#include <bits/stdc++.h>

typedef long long ll;

using namespace std;

int main(void) {
	// For fast IO
	cin.tie(NULL);
	ios_base::sync_with_stdio(false);

	int a, b;
	cin >> a >> b;
	cout << a * b;

	return 0;
}

  B번 Multiplication 2 - 성공

더보기
N개의 정수가 주어졌을 때, 각각의 정수를 모두 곱했을 때, 곱한 값을 출력하시오.
만약 그 값이 10^18을 초과할 경우 -1을 대신 출력하시오.

 

B번 문제의 경우 두 가지를 주의하면 된다고 생각했다.

 

첫번째는 값에 0이 존재 할 경우, 나머지 값과 상관없이 답은 0이라는 것이고,

 

두번째는 계산 도중에 값이 long long 범위를 초과할 수 있다는 것이다.

 

어떻게 구현할까 고민했는데,

 

1. 처음에 값을 배열에 입력받고, 0이 있는지 확인한다.

2. 0이 존재한다면 답은 0

3. 0이 없다면 배열을 정렬해준다.

4. 이후 작은 원소부터 최대 범위 이하인지 확인하면서 곱해간다.

5. 만약 범위를 초과할 경우 답은 -1

6. 초과하지 않았다면 곱한 값을 출력한다.

 

위와 같이 구현했다.

 

주의해야 할 점은 4번에서 범위를 확인할 때,

 

(현재까지의 곱) * (이번에 곱할 수) < (최대 범위)

 

로 확인하면, 좌변에서 범위를 초과할 수 있다.

 

따라서 해당 식을

 

(이번에 곱할 수) < (최대 범위) / (현재까지의 곱)

 

으로 표현해서 사용하였다.

 

아래는 정답코드이다.

 

#include <bits/stdc++.h>

typedef long long ll;

using namespace std;

int main(void) {
	// For fast IO
	cin.tie(NULL);
	ios_base::sync_with_stdio(false);

	long double n, cur = 1, maxnum = 1000000000000000000;
	vector<long double> nums;
	cin >> n;
	
	for(int i = 0; i < n; i++) {
		long double num;
		cin >> num;
		if(num == 0) {
			cout << 0;
			return 0;
		}
		nums.push_back(num);
	}

	sort(nums.begin(), nums.end());

	for(int i = 0; i < n; i++) {
		if(nums[i] <= (maxnum / cur)) {
			cur *= nums[i];
		} else {
			cout << -1;
			return 0;
		}
	}
	
	cout << (long long)cur;

	return 0;
}

C번 Multiplication 3 - 성공

더보기
정수 A와 소수점 둘째자리까지 표현하는 소수 B를 곱한 값을 소수점 이하 값을 제외하고 출력하시오.

 

두 값을 실수형으로 입력받아서 곱한 후, 출력할 때 정수형으로 출력하면 되는 문제이다.

 

#include <bits/stdc++.h>

typedef long long ll;

using namespace std;

int main(void) {
	// For fast IO
	cin.tie(NULL);
	ios_base::sync_with_stdio(false);

	long double a, b;
	cin >> a >> b;
	
	cout << (ll)(a * b);

	return 0;
}

D번 Div Game - 성공

더보기
정수 N이 주어진다. 정수 z를 선택한다.
z는 N의 약수이며, 소수의 e제곱수이다. (z = p^e)
z는 단 한번만 선택할 수 있으며, 이후 N값을 N/z로 변경한다.
이러한 과정을 반복할 수 있는 최대 횟수를 출력하시오.

 

처음에 문제를 보자마자, 에라토스테네스의 체로 풀면 되겠거니 싶어서 풀어봤는데,

정수 N의 값이 10^12까지 가능해서, 배열의 최대 크기가 10^6까지 늘어나서 구현에 실패했다.

 

그래서 일단은 10^5 이하의 소수들을 찾아서, 소수들의 e제곱수로 N을 나눠주는 작업을 진행했다.

 

이후 만약 남은 N이 1이라면, 현재까지의 카운트를 출력하면 되고,

 

N이 1보다 크다면, 10^5보다 큰 소수들이 N의 약수에 존재한다고 판단한다.

 

그래서 (10^5 + 1)부터 10^6 이하까지의 값들이 N을 나누는지 각각 확인하였고,

만약 i가 N의 약수라면, N을 i로 나눌 수 없을 때 까지 나누고, 카운트를 +1 추가한다.

(왜냐하면 소수 i가 세 번 곱해져 있다면, 10^12 범위를 초과하기 때문에, i는 최대 두 번 까지 곱해질 수 있는데, z는 단 한번만 선택할 수 있기 때문에, 결과적으로 한 번만 카운트 할 수 있다.)

 

이후 10^6까지 반복했는데, N값이 1이 아니라면, 남은 N 자기자신이 10^6 이상의 소수라고 판단한다.

 

따라서 위의 과정을 진행하고 카운트를 출력하면 된다.

 

3번정도 틀리고 맞았기 때문에 소스코드가 난잡하지만, 첨부해보면 다음과 같다..

 

#include <bits/stdc++.h>

typedef long long ll;

using namespace std;

int main(void) {
	// For fast IO
	// cin.tie(NULL);
	// ios_base::sync_with_stdio(false);


	ll n;
	ll cnt = 0;
	int arr[100000 + 1] = {0};
	vector<ll> pnums;

	cin >> n;
	
	// 소수 찾기
	for(ll i = 2; i <= 100000 + 1; i++) {
		if(arr[i] != 0) continue;
		if(n % i == 0) pnums.push_back(i);
		for(ll j = 2; i * j <= 100000 + 1; j++) {
			arr[i * j] = 1;
		}
	}

	for(int i = 0; i < pnums.size(); i++) {
		ll p = pnums[i];
		ll cur = p;
		while(n % cur == 0) {
			n /= cur;
			cur *= p;
			cnt++;
		}
		while(n % p == 0) {
			n /= p;
		}
	}

	if(1 < n) {
		// 100000 + 1 보다 크거나 같은 값으로 곱해져 있음.
		for(ll i = 100000 + 1; i < 1000000 + 1 && 1 < n; i++) {
			while(n % i == 0) {
				n /= i;
				cnt++;
			}
		}
		if(1 < n) cnt++;	// n은 소수
	}

	cout << cnt;

	return 0;
}