문제

 

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등을 사용하여 메모리를 절약할 수 있다.

문제

 

1043번: 거짓말

지민이는 파티에 가서 이야기 하는 것을 좋아한다. 파티에 갈 때마다, 지민이는 지민이가 가장 좋아하는 이야기를 한다. 지민이는 그 이야기를 말할 때, 있는 그대로 진실로 말하거나 엄청나게

www.acmicpc.net

 

풀이 전략

  • 한 파티 안에 진실을 아는 사람이 없다면, 그 파티에서는 거짓말을 할 수 있다.
  • 한 파티 안의 멤버 하나라도 진실을 안다면, 그 파티 사람들은 모두 진실을 아는 사람이 된다.
    • 바이러스 전염과 유사.
  • 각 파티를 set으로 구성하여, vector에 넣어둔다.
  • 진실을 아는 사람을 Queue(Vector로 구현함)에 넣어둔다.
  • Queue가 비거나 남은 파티가 없어질때까지, 다음 행동을 반복한다.
    • Queue에서 한 사람을 꺼냄
    • 남은 파티에 해당 인원이 참가한 경우, 다음 행동 수행
      • 해당 파티에 있는 아직 진실을 모르는 사람을 Queue에 집어넣음
      • 해당 파티를 파티 Vector에서 제거
  • 루프가 끝난 후, Vector 내에 남은 파티의 수가 정답이다.

 

전체 소스 코드

더보기
//Backjoon Problem No.1043
//https://www.acmicpc.net/problem/1043
//Mist16, 2022-03-01
#include <iostream>
#include <vector>
#include <set>

using namespace std;

int M = 0, N = 0;

int isAffected[51] = { 0, };

vector<set<int>> vParty;
vector<int> vAffected;

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

	cin >> N >> M >> numb;
	//진실을 아는 사람
	for (int i = 0; i < numb; i++)
	{
		cin >> temp;
		vAffected.push_back(temp);
		isAffected[temp] = 1;
	}
	//파티 입력
	for (int i = 0; i < M; i++)
	{
		set<int> cParty;

		cin >> numb;
		for (int j = 0; j < numb; j++)
		{
			cin >> temp;
			cParty.insert(temp);
		}
		vParty.push_back(cParty);
	}

	while (!vAffected.empty() && !vParty.empty())
	{
		int aPerson = vAffected.back();

		vAffected.pop_back();

		for (auto iter = vParty.begin(); iter != vParty.end(); iter)
		{
			if (iter->find(aPerson) != iter->end())
			{
				for (int i : *iter)
				{
					if (isAffected[i] == 0)
					{
						isAffected[i] = 1;
						vAffected.push_back(i);
					}
				}
				iter = vParty.erase(iter);
			}
			else
				iter++;
		}

	}
	cout << vParty.size();
	return 0;
}

 

Note

 

문제

 

9663번: N-Queen

N-Queen 문제는 크기가 N × N인 체스판 위에 퀸 N개를 서로 공격할 수 없게 놓는 문제이다. N이 주어졌을 때, 퀸을 놓는 방법의 수를 구하는 프로그램을 작성하시오.

www.acmicpc.net

 

풀이 전략

  • 퀸의 경우 상하좌우, 대각선으로 길이 제한 없이 이동할 수 있다.
    • -> 한 줄에 퀸은 하나만 존재할 수 있다.
  • 1~N번째 행까지, 퀸을을 놓을 수 있는 모든 열에 퀸을 두어서, 경우의 수를 모두 더한다.
int queenBoard(int idx, QueenBoard qBoard)
{
	int answer = 0;

	if (idx == N)
		return 1;
	for (int i = 0; i < N; i++)
	{
    	answer += queenBoard(idx + 1, tempBoard);
	}
    return answer;
}
  • 대각선 / 다른 행에 다른 줄의 퀸이 있을 경우 퀸을 놓을 수 없으므로, 퀸을 둘 때마다 보드에 해당 범위를 체크한다.
    • int [N][N]으로 구현할 경우 함수 호출 인자가 지나치게 기므로, 비트마스킹으로 구현하였다.
    • 퀸을 놓기 전, 해당 칸의 비트마스크가 0인지 확인하고, 0일 경우에 해당 퀸에 영향을 받는 칸을 비트마스킹 한 후 함수를 재귀 호출해준다.
    • 비트마스킹해야할 사항은 다음과 같다
      • Queen을 놓은 곳의 가로선
      • Queen을 놓은 곳으로부터의 대각선(X모양)
if ((qBoard.n[i] & (1 << idx)) == 0)
{
	QueenBoard tempBoard = qBoard;
	tempBoard.n[i] = tempBoard.n[i] | (1 << idx);
	for (int j = 1; j < N - idx; j++)
	{
		tempBoard.n[i] = tempBoard.n[i] | (1 << (idx+j));
	
		if (i - j >= 0)
		{
			tempBoard.n[i - j] = tempBoard.n[i - j] | (1 << (idx + j));
		}
		if (i + j < N)
		{
			tempBoard.n[i + j] = tempBoard.n[i + j] | (1 << (idx + j));
		}
	}
}

전체 소스 코드

더보기
더보기
//Backjoon Problem No.09663
//https://www.acmicpc.net/problem/9663
//Mist16, 2022-02-06
#include <iostream>

using namespace std;

int N;

struct QueenBoard
{
	unsigned int n[15] = { 0, };
};

int queenBoard(int idx, QueenBoard qBoard)
{
	int answer = 0;

	if (idx == N)
		return 1;
	for (int i = 0; i < N; i++)
	{
		if ((qBoard.n[i] & (1 << idx)) == 0)
		{
			QueenBoard tempBoard = qBoard;
			tempBoard.n[i] = tempBoard.n[i] | (1 << idx);
			for (int j = 1; j < N - idx; j++)
			{
				tempBoard.n[i] = tempBoard.n[i] | (1 << (idx+j));
				
				if (i - j >= 0)
				{
					tempBoard.n[i - j] = tempBoard.n[i - j] | (1 << (idx + j));
				}
				if (i + j < N)
				{
					tempBoard.n[i + j] = tempBoard.n[i + j] | (1 << (idx + j));
				}
			}
			answer += queenBoard(idx + 1, tempBoard);
		}
	}
	return answer;
}


int main()
{
	QueenBoard blankboard;
	//입출력 속도 증가
	ios::sync_with_stdio(false);
	cin.tie(NULL);
	cout.tie(NULL);
	
	cin >> N;
	cout << queenBoard(0, blankboard);
	
	return 0;
}

 

Note

  • 비트마스킹을 쓰지 않을 경우 시간 초과로 실패하였다. 함수 호출이 잦을 경우, 함수로 전달되는 인자의 양에도 신경을 써야 할 것.

문제

 

11053번: 가장 긴 증가하는 부분 수열

수열 A가 주어졌을 때, 가장 긴 증가하는 부분 수열을 구하는 프로그램을 작성하시오. 예를 들어, 수열 A = {10, 20, 10, 30, 20, 50} 인 경우에 가장 긴 증가하는 부분 수열은 A = {10, 20, 10, 30, 20, 50} 이

www.acmicpc.net

풀이 전략

  • for문을 사용하여, 현재 인덱스 앞에서 자기보다 큰, 방문할 수 있는 위치를 확인하여 진행한다.
    • 20 20 50 10 70 -> 20에서 50을 방문할 경우, 70 방문하는 경우로 재귀 함수 호출
      • 20 20 50 10 70 -> 70을 새로 방문 가능
      • 20 20 50 10 70 -> 더 방문할 곳이 없으므로 종료됨
  • 해당 index를 방문할 때의 길이를 기록하여, 더 작은 부분수열로 방문하지 않도록 한다
    • A : 10 20 30 50 70  / i=4의 Length = 5 기록
    • B : 10 20 30 50 70  / i=4의 Length = 3 : 기록된 Length = 5보다 작으므로 방문하지 않음
  • 일 때, 방문시 최대 길이를 기록해 둘 경우 Length = 5 에서만 방문하면 된다.
  • 재귀함수로 구현하고, for loop를 사용한 호출이 끝난 후 최대 길이를 확인하여 기록한다.
void run(int idx, int current, int length)
{
	for (int i = idx; i < N; i++)
	{
		if (current < A[i] && visitLength[i] < length+1)
		{
			visitLength[i] = length + 1;
			run(i, A[i], length + 1);
		}
	}
	if (maxLength < length)
	{
		maxLength = length;
	}

}

전체 소스 코드

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

#include <iostream>

using namespace std;

int A[1000]= { 0, };
int visitLength[1000] = { 0, };
int N = 0;

int maxLength = 0;

void run(int idx, int current, int length)
{
	for (int i = idx; i < N; i++)
	{
		if (current < A[i] && visitLength[i] < length+1)
		{
			visitLength[i] = length + 1;
			run(i, A[i], length + 1);
		}
	}
	if (maxLength < length)
	{
		maxLength = length;
	}

}

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

	cin >> N;

	for (int i = 0; i < N; i++)
	{
		cin >> A[i];
	}
	run(0, 0, 0);
	cout << maxLength;

	return 0;
}

Note

  • 조건을 검사하지 않고, 방문하지 않을 경우에도 재귀함수를 호출할 경우에는 O(n!)가 되어 시간 초과로 실패하는 문제였다.

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

[백준][C++]1043 - 거짓말  (0) 2022.03.03
[백준][C++]9663 - N-Queen  (0) 2022.03.02
[백준][C++]10830 - 행렬 제곱  (0) 2022.02.05
[백준][C++]1007 - 벡터 매칭  (0) 2022.01.25
[백준][C++]17070 - 파이프 옮기기 1  (0) 2022.01.22

문제

 

10830번: 행렬 제곱

크기가 N*N인 행렬 A가 주어진다. 이때, A의 B제곱을 구하는 프로그램을 작성하시오. 수가 매우 커질 수 있으니, A^B의 각 원소를 1,000으로 나눈 나머지를 출력한다.

www.acmicpc.net

 

풀이 전략

  • A^n = A^(n/2) * A^(n/2) 인 점을 이용하여, 분할 정복한다.
  • 분할 정복으로 곱셈 문제를 해결할 경우 중복된 계산이 많이 발생하므로, 제곱 계산의 결과를 메모이제이션하여 중복된 계산을 생략한다.
  • 이 때, N이 최대 100,000,000,000이고 분할당 1/2으로 감소하므로, Array 대신 Map을 사용하여 결과를 저장하도록 한다.

전체 소스 코드

더보기
//Backjoon Problem No.10830
//https://www.acmicpc.net/problem/10830
//Mist16, 2022-02-05
#include <iostream>
#include <map>

using namespace std;

unsigned int N = 0;
unsigned long long B = 0;

struct matrix
{
	unsigned int mat[5][5];

	matrix()
	{
		for (int i = 0; i < 5; i++)
			for (int j = 0; j < 5; j++)
				mat[i][j] = 0;
	}
};

map<unsigned long long, matrix> dices;

matrix matrixMulti(const matrix& a, const matrix& b)
{
	matrix temp;

	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < N; j++)
		{
			unsigned int tempv = 0;
			for (int k = 0; k < N; k++)
			{
				tempv = tempv + (a.mat[i][k] * b.mat[k][j]);
			}
			tempv = tempv % 1000;
			temp.mat[i][j] = tempv;

		}
	}
	return temp;
}

matrix getNSquare(unsigned long long n)
{
	auto iter = dices.find(n);

	if (iter == dices.end())
	{
		unsigned long long a, b;
		a = n / 2;
		b = n - a;
		matrix tempmat;
		tempmat = matrixMulti(getNSquare(a), getNSquare(b));
		dices.insert(pair <unsigned long long, matrix>(n, tempmat));
		return tempmat;

	}
	return iter->second;
}

void run()
{
	matrix result = getNSquare(B);
	
	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < N; j++)
		{
			cout << result.mat[i][j] % 1000 << " ";
		}
		cout << endl;
	}
	return;
}

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

	matrix temp;
	
	cin >> N >> B;

	for (int i = 0; i < N; i++)
	{
		for (int j = 0; j < N; j++)
		{
			cin >> temp.mat[i][j];
		}
	}

	dices.clear();
	dices.insert(pair <unsigned long long, matrix>(1, temp));

	run();

	return 0;
}

풀이 중 주의 사항

  • N의 범위가 0 ~ 100,000,000,000이므로, 충분히 큰 변수 형태를 사용하여 저장한다.
  • 행렬의 구성요소가 1000으로 입력되고, 제곱연산을 하지 않을 경우에도 0으로 출력해야 하는 점을 주의하도록 한다.

+ Recent posts