문제 링크: https://www.acmicpc.net/problem/1036

문제번호가 1000+36 이라서 36진수 문제인 것 같습니다.

진법변환에 익숙하지 않은 사람한테는 좋은 연습문제가 될 수도 있겠습니다...

 

좀 여러가지를 생각해봐야 하는데, 일단 단어의 길이가 최대 50이기 때문에 C++의 정수형으로 표현하기 곤란합니다. 파이썬이나 자바는 BigInteger가 있으니 그거 쓰면 될 것 같고요, C++은 직접 구현해줘야 합니다.

 

그리고 각 자릿수별로 diff=digit의 출현빈도*(Z와 digit의 차이)를 계산해 diff 상위 k개 digit를 뽑는 방법을 생각해볼 수 있겠는데요, 반례가 있습니다.

//입력
50
XWW
WW
WW
..
WW
1

//출력 (W->Z로 바꿨을 때)
2AYM

자릿수별로 뽑으면 위 입력에서는 제일 높은 자릿수에 있는 X를 그냥 뽑아버리겠죠.

즉, 자릿수별로 차이를 계산하면 안되고, 모든 자릿수의 차이를 누적해 계산해야 합니다.

 

알고리즘은 대략 아래와 같습니다.

  • 1. int histogram[51][36]
    [자릿수][digit]의 개수를 저장하는 배열을 만들고 0으로 초기화
  • 2. 단어 한개씩 입력받을 때마다 히스토그램에 누적
    • 이때 histogram이 36개가 되는 자리수가 생기면 덧셈할 때처럼 0으로 만들고 carry를 올린다
  • 3. 이제 히스토그램을 이용해 각 digit를 Z로 바꿨을 때 값이 얼마나 늘어나는지 계산한다
    • 이 값은 long long 범위를 훌쩍 넘어가니 정수형은 사용 불가
  • 4. 값이 가장 많이 늘어나는 digit k개를 골라 단어들의 해당 digit를 Z로 바꾼다
  • 5. 단어들을 전부 더한 값을 출력한다

 

#pragma GCC optimize ("Ofast")

#define _CRT_SECURE_NO_WARNINGS
#include <bits/stdc++.h>
using namespace std;



int histogram[51][36];	//[자릿수][해당digit] 개수 (0~35)


//36(digit) -> dec
constexpr int trans_digit(char n) {
	if (n>='0' && n<='9')
		return n-'0';
	return n - 'A' + 10;
}

//dec(digit) -> 36
constexpr char rtrans_digit(int n) {
	if (n < 10)
		return static_cast<char>(n+'0');
	return static_cast<char>(n-10+'A');
}

//dec(num) -> 36
string rtrans(int n) {
	string ret;

	while (n) {
		ret += rtrans_digit(n % 36);
		n /= 36;
	}

	reverse(ret.begin(), ret.end());
	return ret;
}

//36(num) + 36(num)
string add(string a, string b) {
	// a>=b가 되도록 (자릿수)
	if (b.size() > a.size())
		swap(a, b);

	// b 앞에 0으로 채움
	b = string(a.size()-b.size(), '0') + b;

	string ret;

	int carry=0;
	for (auto it1=a.rbegin(), it2=b.rbegin();
		it1 != a.rend();
		++it1, ++it2)
	{
		int sum = carry + trans_digit(*it1) + trans_digit(*it2);
		carry = sum / 36;
		sum %= 36;

		ret += rtrans_digit(sum);
	}

	if (carry)
		ret += '1';

	reverse(ret.begin(), ret.end());
	return ret;
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("input.txt", "r", stdin);
#endif

	ios_base::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);

	int n, k;
	cin >> n;

	vector<string> words(n);
	for (auto& w : words) {
		cin >> w;

		int counter = 0;	//자릿수
		for (auto rit = w.rbegin();
			rit != w.rend();
			++rit, ++counter) {
			int digit = trans_digit(*rit);
			++histogram[counter][digit];

			// carry 발생
			if (histogram[counter][digit] == 36) {
				int current_idx = counter;
				while (histogram[current_idx][digit] == 36) {
					histogram[current_idx][digit] = 0;
					++current_idx;
					++histogram[current_idx][digit];
				}
			}
		}
	}

	cin >> k;
	vector<char> candidates;	// Z로 바꿀거

	vector<pair<string, int>> vp;	// diff, digit_idx
	for (int i = 0; i < 35; ++i) {	//z 빼고
		string diff;
		for (int j = 0; j < 51; ++j) {
			if (histogram[j][i]) {
				string s = rtrans(histogram[j][i] * (35-i)) + string(j, '0');
				diff = add(diff, s);
			}
		}

		vp.emplace_back(diff, i);
	}

	sort(vp.begin(), vp.end(), [](auto& lhs, auto& rhs) {
		auto &[s1, ign1] = lhs;
		auto &[s2, ign2] = rhs;
		
		if (s1.size() != s2.size())
			return s1.size() > s2.size();

		return s1 > s2;
	});

	for (int i = 0; i < min(k,35); ++i)
		candidates.push_back(rtrans_digit(vp[i].second));


	// 알파벳 변환
	for (auto& w : words) {
		for (auto& c : candidates) {
			replace(w.begin(), w.end(), c, 'Z');
		}
	}

	// 더함 (36진수)
	string ans;
	for (auto& w : words)
		ans = add(ans, w);
	cout << ans << '\n';

	return 0;
}

코드가 좀 기네요. 짜는데 힘을 다 쏟아버렸습니다..

반응형

'Online Judge > 백준' 카테고리의 다른 글

[백준][C++] 4836: 춤  (0) 2020.07.02
[백준][C++] 1043: 거짓말  (0) 2020.06.29
[백준][C++] 1036: 36진수  (0) 2020.06.24
[백준][C++] 18119: 단어 암기  (0) 2020.06.21
[백준][C++] 17081: RPG Extreme  (2) 2020.06.16
[백준][C++] 18111: 마인크래프트  (0) 2020.06.15