문제

 

2467번: 용액

첫째 줄에는 전체 용액의 수 N이 입력된다. N은 2 이상 100,000 이하의 정수이다. 둘째 줄에는 용액의 특성값을 나타내는 N개의 정수가 빈칸을 사이에 두고 오름차순으로 입력되며, 이 수들은 모두 -

www.acmicpc.net

풀이 전략

  • 2중 for문을 사용할 경우에는 O(N^N)이 되게 되어 시간 초과에 걸리게 된다.
  • 입력값이 오름차순으로 입력되는것을 이용하여, 인덱스 두개를 사용하여 계산한다.
  • 양쪽 끝에 각 인덱스를 둔 다음, 두 용액의 합이 양수라면 오른쪽 인덱스를, 음수라면 왼쪽 인덱스를 한 칸 중앙으로 당긴다.
  • 해당 계산은 두 인덱스가 겹치거나, 두 용액의 합이 0이 될 경우 종료한다.
  • 0에 가장 가까운 용액합이 나왔을 경우 인덱스를 저장해뒀다가, 정답으로 출력한다.

EX)

-7 -4 3 6 9   -> -7 + 9 = 4 < 0 : 9를 6으로 옮긴다

-7 -4 3 6 9   -> -7 + 6 = -1 > 0 : -7를 -4으로 옮긴다

 

전체 소스 코드

더보기
//Backjoon Problem No.2467
//https://www.acmicpc.net/problem/2467

#include <iostream>

using namespace std;

int main()
{
	int N;
	int liquids[100000] = { 0, };
	int idxA, idxB;
	int minValue = 2000000001;
	int answerA, answerB;
	//입출력 속도 증가
	ios::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);

	cin >> N;

	for (int i = 0; i < N; i++)
		cin >> liquids[i];

	idxA = 0;
	idxB = N - 1;

	while (idxA != idxB)
	{
		int temp;
		temp = liquids[idxA] + liquids[idxB];
		if (temp > 0)
		{
			if (temp < minValue)
			{
				minValue = temp;
				answerA = liquids[idxA];
				answerB = liquids[idxB];
			}
			idxB--;
		}
		else if (temp < 0)
		{
			temp = temp * -1;
			if (temp < minValue)
			{
				minValue = temp;
				answerA = liquids[idxA];
				answerB = liquids[idxB];
			}
			idxA++;
		}
		else
		{
			minValue = 0;
			answerA = liquids[idxA];
			answerB = liquids[idxB];
			break;
		}
	}

	cout << answerA << " " << answerB;

	return 0;
}

 

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

[백준][C++]9465 - 스티커  (0) 2022.03.14
[백준][C++]1036 - 36진수  (0) 2022.03.08
[백준][C++]1562 - 계단 수  (0) 2022.03.08
[백준][C++]1043 - 거짓말  (0) 2022.03.03
[백준][C++]9663 - N-Queen  (0) 2022.03.02

문제

 

9465번: 스티커

첫째 줄에 테스트 케이스의 개수 T가 주어진다. 각 테스트 케이스의 첫째 줄에는 n (1 ≤ n ≤ 100,000)이 주어진다. 다음 두 줄에는 n개의 정수가 주어지며, 각 정수는 그 위치에 해당하는 스티커의

www.acmicpc.net

 

풀이 전략

  • i번째 줄의 스티커를 뗄 때, 경우의 수는 다음과 같이 나타낼 수 있다
    • i-1번째 줄에서 스티커를 떼지 않음
    • i-1번째 줄에서 위쪽 줄 스티커를 뗌
    • i-1번째 줄에서 위쪽 줄 스티커를 떼지 않음.
  • 그리고, i-1번쨰 줄에서 다음 상태였을때, i번째 줄의 각 상태로 이행할 수 있다.
    • i-1번째 줄에서 떼지 않음/위쪽을 뗌 -> 아래쪽을 뗄 수 있음
    • i-1번째 줄에서 뗴지 않음/아래쪽을 뗌 -> 위쪽을 뗄 수 있음
    • 모든 경우의 수 -> 떼지 않음
      • 하지만 i-1번째 줄에서 떼지 않은 경우의 스코어 < i-1번째 줄에서 위쪽/아래쪽을 뗀 경우의 스코어이기 때문에, 위쪽/아래쪽을 뗀 경우만만을 비교한다
  • for루프를 사용하여 1~N번째 줄까지, 3가지 상태에 대하여 도출가능한 이전 상태 중 높은 값을 선택하는 식으로 계산을 이어나간다.
// 0 : 위쪽을 뗌
// 1 : 아래쪽을 뗌
// 2 : 떼지 않음
unsigned int stickerMap[100000][2];
unsigned int maxPoint[100000][3];

	for (int i = 1; i < N; i++)
	{
		//up
		if (maxPoint[i-1][1] > maxPoint[i-1][2])
		{
			maxPoint[i][0] = maxPoint[i - 1][1] + stickerMap[i][0];
		}
		else
			maxPoint[i][0] = maxPoint[i - 1][2] + stickerMap[i][0];
		//down
		if (maxPoint[i - 1][0] > maxPoint[i - 1][2])
		{
			maxPoint[i][1] = maxPoint[i - 1][0] + stickerMap[i][1];
		}
		else
			maxPoint[i][1] = maxPoint[i - 1][2] + stickerMap[i][1];
		//none
		if (maxPoint[i - 1][0] > maxPoint[i - 1][1])
			maxPoint[i][2] = maxPoint[i - 1][0];
		else
			maxPoint[i][2] = maxPoint[i - 1][1];
	}
  • 계산이 끝난 후, maxPoint[N-1]의 0~2번째 값 중 큰 값을 리턴한다.

전체 소스 코드

더보기
#include <iostream>
#include <memory.h>
using namespace std;

unsigned int stickerMap[100000][2];
unsigned int maxPoint[100000][3];

int testCases = 0;
int N = 0;

void run()
{
	maxPoint[0][0] = stickerMap[0][0];
	maxPoint[0][1] = stickerMap[0][1];
	maxPoint[0][2] = 0;

	for (int i = 1; i < N; i++)
	{
		//up
		if (maxPoint[i-1][1] > maxPoint[i-1][2])
		{
			maxPoint[i][0] = maxPoint[i - 1][1] + stickerMap[i][0];
		}
		else
			maxPoint[i][0] = maxPoint[i - 1][2] + stickerMap[i][0];
		//down
		if (maxPoint[i - 1][0] > maxPoint[i - 1][2])
		{
			maxPoint[i][1] = maxPoint[i - 1][0] + stickerMap[i][1];
		}
		else
			maxPoint[i][1] = maxPoint[i - 1][2] + stickerMap[i][1];
		//none
		if (maxPoint[i - 1][0] > maxPoint[i - 1][1])
			maxPoint[i][2] = maxPoint[i - 1][0];
		else
			maxPoint[i][2] = maxPoint[i - 1][1];
	}

}

int main()
{
	int numb, temp;
	//입출력 속도 증가
	ios::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);

	cin >> testCases;

	for (int i = 0; i < testCases; i++)
	{
		unsigned int tempMaxValue = 0;
		//input
		cin >> N;
		for (int j = 0; j < N; j++)
		{
			cin >> stickerMap[j][0];
		}
		for (int j = 0; j < N; j++)
		{
			cin >> stickerMap[j][1];
		}
		//run
		run();

		for(int j = 0; j < 3; j++)
		{
			if (tempMaxValue < maxPoint[N-1][j])
				tempMaxValue = maxPoint[N-1][j];
		}
		cout << tempMaxValue << "\n";
	}
	return 0;
}

 

Note

  • 이전 계산값을 사용하지 않기때문에, maxPoint를 배열 대신 변수 6개(current, next)로 사용하는 방법도 생각할만했다. 다만 그 경우 계산량이 부족하여, 문제의 메모리 제한(256MB)을 최대한 활용하는 방향으로 풀이했다.
  • 입력을 받아오는 양이 많기때문에, sync_with_studio, cin.tie(NULL)을 사용하여 입력을 최적화해주지 않으면 시간제한에 걸린다.

 

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

[백준][C++]2467 - 용액  (0) 2022.04.06
[백준][C++]1036 - 36진수  (0) 2022.03.08
[백준][C++]1562 - 계단 수  (0) 2022.03.08
[백준][C++]1043 - 거짓말  (0) 2022.03.03
[백준][C++]9663 - N-Queen  (0) 2022.03.02

문제

 

1036번: 36진수

첫째 줄에 수의 개수 N이 주어진다. 둘째 줄부터 N개의 줄에 수가 주어진다. N은 최대 50이고, 수의 길이도 최대 50이다. 마지막 줄에 K가 주어진다. K는 36보다 작거나 같은 자연수 또는 0이다.

www.acmicpc.net

풀이 전략

  • 36진수를 저장할 수 있는 구조체를 생성한 후, 해당 구조체를 사용하는 덧셈 함수를 만든다
  • 각 36진수를 입력받을때, sum 이외의 각 자리수가 Z로 바뀌었을때 더해지는 값을 저장할 배열을 생성하여 관리한다.

EX : 36진수 'Z120'

struct sum, digitsum[36]

sum += 'Z120'

 Z = (Z-Z) * 36^3 -> digitsum[Z->35]에 더함

1 = (Z-1) * 36^2 -> digitsum[1]에 더함

2 = (Z-2) * 36 -> digitsum[2]에 더함

0 = (Z-0) * 1 -> digitsum[0]에 더함

  • 모든 단어를 입력받은 후, 배열을 크기순으로 정렬하여 N개의 36진수를 SUM값에 더한다.

전체 소스 코드

더보기
//Backjoon Problem No.1036
//https://www.acmicpc.net/problem/1036
#include <iostream>
#include <string>
#include <queue>

using namespace std;

struct decimal36
{
	unsigned int cipher[60] = { 0, };
	int maxDigit = 0;

	bool operator<(const decimal36& t) const
	{
		if (maxDigit != t.maxDigit)
			return maxDigit < t.maxDigit;
		for (int i = maxDigit; i >= 0; i--)
		{
			if (cipher[i] != t.cipher[i])
				return cipher[i] < t.cipher[i];
		}
		return false;
	}
};

priority_queue<decimal36> decQueue;

decimal36 byCipher[36];
decimal36 total;

void arrangeCipher(decimal36 &de)
{
	for (int i = 0; i < 60; i++)
	{
		if (de.cipher[i] > 0)
		{
			de.maxDigit = i;
			if (de.cipher[i] > 35)  //
			{
				de.cipher[i+1] += (int)(de.cipher[i] / 36);
				de.cipher[i] = de.cipher[i] % 36;
			}
		}
	}
}

int charTode36(char c)
{
	int a = (int)c;
	if (a < 60) //0 = 48, 9 = 57;
		return a - 48;
	else //A = 65; Z = 90;
		return a - 55;
}
char cipherTochar(int i)
{
	if (i < 10)
		return (char)(i + 48);
	else
		return (char)(i + 55);
}

int N, K;

int main()
{
	//입출력 속도 증가
	ios::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);

	cin >> N;

	for (int i = 0; i < N; i++)
	{
		string temp;
		cin >> temp;
		if (temp.size() == 1 && temp.at(0) == '0')
			continue;
		for (int j = 0; j < temp.size(); j++)
		{
			int target = charTode36(temp.at(j));
			byCipher[target].cipher[temp.size() - j - 1] += 35 - target;
			total.cipher[temp.size() - j - 1] += target;
		}
	}

	for (int i = 0; i < 36; i++)
	{
		arrangeCipher(byCipher[i]);
		decQueue.push(byCipher[i]);
	}
	cin >> K;

	for (int i = 0; i < K; i++)
	{
		decimal36 temp = decQueue.top();
		for (int j = 0; j < 60; j++)
		{
			total.cipher[j] = temp.cipher[j] + total.cipher[j];
		}
		decQueue.pop();
	}
	arrangeCipher(total);

	for (int i = total.maxDigit; i >= 0; i--)
	{
		cout << cipherTochar(total.cipher[i]);
	}

	return 0;

Note

 

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

[백준][C++]2467 - 용액  (0) 2022.04.06
[백준][C++]9465 - 스티커  (0) 2022.03.14
[백준][C++]1562 - 계단 수  (0) 2022.03.08
[백준][C++]1043 - 거짓말  (0) 2022.03.03
[백준][C++]9663 - N-Queen  (0) 2022.03.02

문제

 

1562번: 계단 수

첫째 줄에 정답을 1,000,000,000으로 나눈 나머지를 출력한다.

www.acmicpc.net

풀이 전략

  • 모든 경우의 수를 구하는 문제
  • 현재 수를 구할 필요는 없다(0~9까지의 숫자가 모두 등장하는지만을 확인하면 된다)
    • 비트마스킹으로 모든 수가 등장하는지를 확인한다.
  • 0~8에서는 다음 수로, 1~9에서는 이전 수로 이동할 수 있다(9->0, 0->9는 불가능하다)
  • 남은 인덱스, 현재 숫자, 비스마스크된 숫자를 함수 인자로 활용한다.
  • unsigned long run(int lastDigit, int idx, unsigned int cBitMask)
  • N의 값이 높을 경우 중복되는 함수 호출이 많아 속도를 확보하지 못하므로, 배열을 사용하여 메모이제이션해준다.
    • 답의 범위는 (0 ~ 999999999) * 2이므로, unsigned long으로 처리 가능하다.
      • *2 하는 이유는 경우의수를 구하는 과정에서 두 run의 값을 더하기 떄문이다.
    • bistmask : 0~1023, idx : 0~100, lastDigit : 0~9 이므로 1024*101*10 = 약 백만개의 변수를 사용한다.
      • 문제의 메모리 제한이 128MB이므로, 충분히 사용 가능하다.
    • 남은 숫자(idx)가 0이고, Digit이 모두 찬 경우(1023)는 경우의 수가 1이므로, 1로 선언하여둔다.
    • idx가 0인 나머지경우는 조건을 만족하지 못했으므로 0으로 처리하고, 남은 경우는 계산하여 저장한다.

전체 소스 코드

더보기
//Backjoon Problem No.1562
//https://www.acmicpc.net/problem/1562
//Mist16, 2022-03-08

#include <iostream>

using namespace std;

unsigned int answerBitmask = 0b1111111111;

unsigned long answerBoard[1024][101][10] = { 0, };

void initialize()
{
	for (int i = 0; i <= 9; i++)
	{
		answerBoard[answerBitmask][0][i] = 1;
	}
}


unsigned long run(int lastDigit, int idx, unsigned int cBitMask)
{
	unsigned long answer = 0;
	if (answerBoard[cBitMask][idx][lastDigit] != 0)
		return answerBoard[cBitMask][idx][lastDigit];
	if (idx == 0)
		return 0;
	//+1
	if (lastDigit < 9)
	{
		answer += run(lastDigit + 1, idx - 1, cBitMask | (1 << (lastDigit + 1)));
	}
	//-1
	if (lastDigit > 0)
	{
		answer += run(lastDigit - 1, idx - 1, cBitMask | (1 << (lastDigit - 1)));
	}	
	answer = answer % 1000000000;
	answerBoard[cBitMask][idx][lastDigit] = answer;
	return answer;
}

int main()
{
	unsigned long long tAnswer = 0;
	unsigned long answer = 0;
	int N;
	cin >> N;

	initialize();

	for (int i = 1; i <= 9; i++)
	{
		answer += run(i, N - 1, (1 << i));
		answer = answer % 1000000000;
	}
	cout << answer;
	return 0;
}

Note

  • 생각보다 큰 사이즈의 배열이라도 메모이제이션을 써야 할 경우가 있다.
    • 경우의 수는 크지만 실제 도출되는 계산의 양이 적은 경우는, STL map등을 사용하여 메모리를 절약할 수 있다.

백준 골드 5 달성.

100문제 남짓을 풀면서, 굳어있던 알고리즘 머리가 조금 말랑해지기는 한 것 같다.

 

앞으로도 실버 상위~골드 문제 위주로, 하루 한두문제는 꾸준히 풀어가자.

다음 목표는 골드3.

'잡담' 카테고리의 다른 글

백준 골드 3 달성  (0) 2022.04.01
프로그래머스 레벨 4 첫 시도, 절반은 했다.  (0) 2022.02.27

+ Recent posts