#include <iostream>
using namespace std;
char DAT[256] = { 0 };

int main()
{
	char cardList[15];
	cin >> cardList;

	for (int i =0; cardList[i] != '\0'; ++i)
	{
		DAT[cardList[i]]++;
	}

	int cnt = 0;
	for (const char& d : DAT)
	{
		if (0 < d) cnt++;
	}

	cout << cnt;
	return 0;
}

 


int main()
{
	// 1. Input
	int rec[3][4] = {
		{65000,35,42,70},
		{70,35,65000,1300},
		{65000,30000,38,42}
	};
	char dat[65536] = { 0 };


	// 2. Solve
	for (int i = 0; i < 3; ++i)
	{
		for (int j = 0; j < 4; ++j)
		{
			++(dat[rec[i][j]]);
		}
	}

	int max = -1;
	int maxIdx = -1;
	for (int i = 0; i < sizeof(dat); ++i)
	{
		if (max < dat[i])
		{
			max = dat[i];
			maxIdx = i;
		}
	}

	// 3. Print
	cout << maxIdx;
	return 0;
}

 


int main()
{
	// 1. Input
	int arr[3][3] = { 0 };
	for (int i = 0; i < 3; ++i)
	{
		for (int j = 0; j < 3; ++j)
		{
			cin >> arr[i][j];
		}
	}

	char flag[10] = { 0 };


	// 2. Solve
	for (int i = 0; i < 3; ++i)
	{
		for (int j = 0; j < 3; ++j)
		{
			flag[arr[i][j]] = 1;
		}
	}


	// 3. Print
	for (int i = 1; i < 10; ++i)
	{
		if (flag[i] == 0)
		{
			cout << i;
		}
	}
	return 0;
}

 


#include <iostream>
using namespace std;

int main()
{
	// 1. Input
	char c[8] = { 0 };
	for (auto& d : c)
	{
		cin >> d;
	}

	char dat['z'] = { 0 };

	// 2. Solve
	for (auto& d : c)
	{
		++(dat[d]);
	}

	char max = -1;
	char maxIdx = -1;
	for (int i = 0; i < sizeof(dat); ++i)
	{
		if (max < dat[i])
		{
			max = dat[i];
			maxIdx = i;
		}
	}

	// 3. Print
	cout << maxIdx;
	return 0;
}

 


#include <iostream>
using namespace std;

int main()
{
	// 1. Input
	char town[3][3] = { 'C','D','A','B','M','Z','Q','P','O'};
	char dat['Z' - 65 + 1] = { 0 };

	for (int i = 0; i < 4; ++i)
	{
		char c; cin >> c;
		++(dat[c - 65]);
	}

	// 2. Solve
	for (int i = 0; i < 3; ++i)
	{
		for (int j = 0; j < 3; ++j)
		{
			++(dat[town[i][j] - 65]);
		}
	}

	int ret = 0;
	for (auto& d : dat)
	{
		if (2 <= d)
		{
			++ret;
		}
	}

	// 3. Print
	cout << ret;
	return 0;
}

 


#include <iostream>
using namespace std;

int main()
{
	// 1. Input
	char arr[3][5] = { 'A','B','C','A','G','H','H','I','J','K','A','B','A','B','C' };

	char dat[256] = { 0 };

	for (int i = 0; i < 3; ++i)
	{
		for (int j = 0; j < 5; ++j)
		{
			++(dat[arr[i][j]]);
		}
	}


	// 2. Solve & Print
	for (int i = 0; i < sizeof(dat); ++i)
	{
		int max = dat[i];
		for (int j = 0; j < max; ++j)
		{
			cout << (char)i;
		}
	}

	return 0;
}

 


#include <iostream>
using namespace std;

int main()
{
	// 1. Input
	int train[8] = { 3,7,6,4,2,9,1,7 };
	int team[3] = { 0 };
	int size = 3;

	for (auto& d : team)
	{
		cin >> d;
	}

	// 2. Solve & Print
	for (int i = 0; i <= train - team; ++i)
	{
		int cnt = 0;
		for (int j = 0; j < size; ++j)
		{
			if (train[i + j] == team[j])
			{
				++cnt;
			}
		}

		if (cnt == 3)
		{
			cout << i << "번 ~ " << i + size - 1 << "번 칸";
			return 0;
		}
	}

	return -1;
}

 


#include <iostream>
using namespace std;

int apt[5][3] = { 15,2,6,7,8,9,10,1,3,4,6,9,15,18,17 };
int maxFloor = 5;
int family[3] = { 0 };
int maxFam = 3;

bool isPattern(int* a, int* b, const int& size)
{
	for (int i = 0; i < size; ++i)
	{
		if (a[i] != b[i])
		{
			return false;
		}
	}

	return true;
}

int main()
{
	// 1. Input
	for (auto& d : family)
	{
		cin >> d;
	}

	// 2. Solve
	for (int i = 0; i < maxFloor; ++i)
	{
		if (isPattern(apt[i], family, maxFam) == true)
		{
			cout << i + 1 << "층";
			return 0;
		};
	}

	return -1;
}

 


int main()
{
	// 1. Input
	int arr[3][5] = { 1,3,3,5,1,3,6,2,4,2,1,9,2,6,5 };
	int n = 0;
	cin >> n;

	char dat[10] = { 0 };

	// 2. Solve & Print
	for (int i = 0; i < 3; ++i)
	{
		for (int j = 0; j < 5; ++j)
		{
			++(dat[arr[i][j]]);
		}
	}

	for (int i = 0; i < 10; ++i)
	{
		if (n == dat[i])
		{
			cout << i << " ";
		}
	}

	return 0;
}

 


#include <iostream>
using namespace std;

void StrCpy(char* to, const char* from)
{
	int i = 0;
	for (i = 0; from[i] != '\0'; ++i)
	{
		to[i] = from[i];
	}
	to[i] = '\0';

	return;
}

class Person 
{
public:
	Person() = default;
	Person(const Person&) = default;
	~Person() = default;

	const char* GetName()const
	{
		return m_name;
	}

	const char* GetAddress()const
	{
		return m_add;
	}

	const char* GetTel()const
	{
		return m_tel;
	}

	const int GetId()const
	{
		return m_id;
	}

	const int GetMileage()const
	{
		return m_mile;
	}

	void SetName(const char* name)
	{
		StrCpy(m_name, name);
	}

	void SetAdd(const char* add)
	{
		StrCpy(m_add, add);
	}

	void SetTel(const char* tel)
	{
		StrCpy(m_tel, tel);
	}

	void SetId(const int id)
	{
		m_id = id;
	}

	void SetMile(const int mile)
	{
		m_mile = mile;
	}

private:

	char m_name[100] = { 0 };
	char m_add[100] = { 0 };
	char m_tel[100] = { 0 };
	int m_id = 0;
	int m_mile = 0;
};

int main()
{
	// 1. Input
	Person p;
	char buf[100] = { 0 };
	int temp = 0;

	cout << "이름을 입력하세요 :";
	cin >> buf;
	p.SetName(buf);

	cout << "주소를 입력하세요 :";
	cin >> buf;
	p.SetAdd(buf);

	cout << "연락처를 입력하세요 :";
	cin >> buf;
	p.SetTel(buf);

	cout << "ID를 입력하세요 :";
	cin >> temp;
	p.SetId(temp);

	cout << "마일리지를 입력하세요 :";
	cin >> temp;
	p.SetMile(temp);

	// 2. Output
	cout << "-----고객 정보-----" << endl;
	cout << "이름: " << p.GetName() << endl;
	cout << "주소: " << p.GetAddress() << endl;
	cout << "연락처: " << p.GetTel() << endl;
	cout << "고객ID: " << p.GetId() << endl;
	cout << "마일리지: " << p.GetMileage() << endl;

	return 0;
}

#include <iostream>
using namespace std;

void Print(const int a, const int b, const int c)
{
	for (int j = 0; j < c; ++j)
	{
		for (int i = a; i <= b; ++i)
		{
			cout << i << ' ';
		}
		cout << endl;
	}
}

int main()
{
	//Input
	int a, b, c;
	cin >> a >> b >> c;

	Print(a, b, c);

	return 0;
}

 


#include <iostream>
using namespace std;

int main()
{
	//Input
	char base = 'A';
	char board[6][3] = { 0 };
	for (int x = 2; x >= 0; x--)
	{
		for (int y = 5; y >= 0; y--)
		{
			board[y][x] = base++;
		}
	}

	int x, y;
	cin >> x >> y;
	cout << board[x][y];

	return 0;
}

 


#include <iostream>
using namespace std;

int arrA[6] = { 0 };
int arrB[2][3];

void FindMin(int& x, int& y)
{
	int min = INT_MAX;

	for (int i = 0; i < 2; ++(i))
	{
		for (int j = 0; j < 3; ++(j))
		{
			if (arrB[i][j] <= min)
			{
				x = i;
				y = j;
				min = arrB[i][j];
			}
		}
	}
}

void FindMax(int& x, int& y)
{
	int max = INT_MIN;

	for (int i = 0; i < 2; ++(i))
	{
		for (int j = 0; j < 3; ++(j))
		{
			if (max <= arrB[i][j])
			{
				x = i;
				y = j;
				max = arrB[i][j];
			}
		}
	}
}

int main()
{
	//Input
	for (int i = 0; i < 6; ++i)
	{
		cin >> arrA[i];
	}
	
	for (int i = 0; i < 2; ++i)
	{
		for (int j = 0; j < 3; ++j)
		{
			arrB[i][j] = arrA[i * 3 + j];
		}
	}

	// Solve & Print
	int x, y;
	FindMax(x, y);
	cout << "(" << x << ", " << y << ")" << endl;
	FindMin(x, y);
	cout << "(" << x << ", " << y << ")" << endl;

	return 0;
}

 


#include <iostream>
using namespace std;

int main()
{
	// Input
	int arr[6] = { 0 };
	cin >> arr[0] >> arr[1];

	// Solve
	for (int i = 1; i < 6 - 1; ++i)
	{
		arr[i + 1] = arr[i] * arr[i - 1];
	}

	// Output
	cout << arr[5];
	return 0;
}

 


#include <iostream>
using namespace std;

int main()
{
	// Input
	char str[100];
	cin >> str;

	char a, b;
	cin >> a >> b;

	// Solve
	for (char& d : str)
	{
		if (d == a)
		{
			d = b; 
		}
		if (d == '\0') break;
	}

	// Output
	cout << str;
	return 0;
}

 


#include <iostream>
using namespace std;

int FindMax(char* str)
{
	int max = str[0];
	int ret = 0;
	for (int i = 1; str[i] != '\0'; ++i)
	{
		if (max < str[i])
		{
			max = str[i];
			ret = i;
		}
	}
	return ret;
}

int FindMin(char* str)
{
	int min = str[0];
	int ret = 0;
	for (int i = 1; str[i] != '\0'; ++i)
	{
		if (str[i] < min)
		{
			min = str[i];
			ret = i;
		}
	}
	return ret;
}

int main()
{
	// Input
	char str[100];
	cin >> str;

	
	// Solve
	cout << FindMax(str) << endl;
	cout << FindMin(str) << endl;
	return 0;
}

 


#include <iostream>
using namespace std;

int main()
{
	// Input
	int arr[7][4];

	int cnt = 1;
	for (int i = 0; i < 7; ++i)
	{
		for (int j = 0; j < 4; ++j)
		{
			arr[i][j] = cnt++;
		}
	}
	
	// Solve
	int iArr[3] = { 0 };
	for (int i = 0; i < 3; ++i)
	{
		cin >> iArr[i];
		memset(arr + iArr[i], 0, sizeof(int) * 4);
	}

	// Print
	for (int i = 0; i < 7; ++i)
	{
		for (int j = 0; j < 4; ++j)
		{
			cout << arr[i][j] << ' ';
		}
		cout << endl;
	}
	return 0;
}

 


#include <iostream>
using namespace std;

char arr[2][6]{
		{'A','7','9','T','K','Q'},
		{'M','I','N','C','O','D'}
};

bool IsExist(char c)
{
	for (int i = 0; i < 2; ++i)
	{
		for (int j = 0; j < 6; ++j)
		{
			if (arr[i][j] == c)
			{
				return true;
			}
		}
	}

	return false;
}

int main()
{
	// Input
	char a, b;
	cin >> a >> b;

	// Solve & Print
	cout << (IsExist(a) ? "존재" : "없음") << endl;
	cout << (IsExist(b) ? "존재" : "없음") << endl;

	return 0;
}

 


#include <iostream>
using namespace std;

int main()
{
	// Input
	int a, b;
	char c;
	cin >> a >> b >> c;
	
	// Solve & Print
	for (int i = 0; i < a; ++i)
	{
		for (int j = 0; j < 2; ++j)
		{
			for (int k = 0; k < b; ++k)
			{
				cout << c;
			}
			cout << endl << endl;
		}
	}

	return 0;
}

 


#include <iostream>
using namespace std;

void StrCpy(char* to, const char* from)
{
	int i;
	for (i = 0; from[i] != '\0'; ++i)
	{
		to[i] = from[i];
	}
	to[i] = '\0';
	return;
}

struct Weapon
{
	Weapon() = default;
	Weapon(const char* str)
	{
		StrCpy(mName, str);
	}
	char mName[100] = { 0 };
};

class Player
{
public:
	Player() = default;
	Player(const Player&) = default;
	~Player() = default;

	void PrintInfo()
	{
		cout << mName << endl;
		cout << "hp : " << mHp << endl;
		cout << "weapon : " << mWeapon.mName << endl;

		return;
	}

	void SetName(const char* name)
	{
		StrCpy(mName, name);
	}

	void SetHp(int hp)
	{
		mHp = hp;
	}

	void SetWeapon(Weapon weapon)
	{
		mWeapon = weapon;
	}

private:
	char mName[100] = { 0 };
	int mHp;
	Weapon mWeapon = Weapon();
};

int main()
{
	// Input
	Player a;
	a.SetName("warrior");
	a.SetHp(200);
	a.SetWeapon(Weapon("sword"));

	Player b;
	b.SetName("archer");
	b.SetHp(100);
	b.SetWeapon(Weapon("bow"));
	
    // Output
	a.PrintInfo();
	b.PrintInfo();
	return 0;
}

 

#include <iostream>
using namespace std;

char FindChar(char* str)
{
	int idx = 0;
	while(str[idx] != '\0')
	{
		++idx;
	}

	return str[idx - 1];
}

int main()
{
	// Input
	char str[3][10];
	for (int i = 0; i < 3; ++i)
	{
		// 널 문자 자동으로 삽입
		cin >> str[i];
	}


	// Solve & Output
	for (int i = 0; i < 3; ++i)
	{
		cout << FindChar(str[i]);
	}

	return 0;
}

 


#include <iostream>
using namespace std;

char c[2] = { 0 };

int FindCharCount(char board[4][4])
{
	int ret = 0;

	for (int i = 0; i < 4; ++i)
	{
		for (int j = 0; j < 4; ++j)
		{
			if (board[i][j] == c[0])
			{
				++ret;
			}
			if (board[i][j] == c[1])
			{
				++ret;
			}
		}
	}
	return ret;
}

int main()
{
	// Input
	char board[4][4] =
	{ {'A', 'B', 'K', 'T'},
	  {'K', 'F', 'C', 'F'},
	  {'B', 'B', 'Q', 'Q'},
	  {'T', 'P', 'Z', 'F'} };


	// Solve & Output
	cin >> c[0];
	cin >> c[1];
	cout << FindCharCount(board);

	return 0;
}

 


#include <iostream>
using namespace std;

int main()
{
	// Input
	char* str = new char[9];
	str += 1;
	cin >> str;
	str -= 1;

	int idx = 0;
	cin >> idx;

	// Sovle
	for (int i = 1; i <= idx; ++i)
	{
		str[i - 1] = str[i];
	}
	str[idx] = 'A';

	//Output
	cout << str;

	return 0;
}

 


#include <iostream>
using namespace std;

int main()
{
	// Input
	int a[4] = { 0 };
	int b[4] = { 0 };

	for (int i = 0; i < 4; ++i)
	{
		cin >> a[i];
	}
	for (int i = 0; i < 4; ++i)
	{
		cin >> b[i];
	}
	
	// Sovle & Output
	int ret[4] = { 0 };
	for (int i = 0; i < 4; ++i)
	{
		ret[i] = a[i] + b[3 - i];
		cout << ret[i] << ' ';
	}

	return 0;
}

 


#include <iostream>
using namespace std;

void Func(char* str, const int& idx)
{
	int i = idx;
	do
	{
		str[i] = str[i + 1];
	} while (str[i++] != '\0');
	
	cout << str;
}

int main()
{
	// Input
	char* str = new char[100];
	cin >> str;

	int idx = 0;
	cin >> idx;


	// Sovle & Output
	Func(str, idx);
	return 0;
}

 


#include <iostream>
using namespace std;

bool IsExist(char (*str)[11])
{
	for (int i = 0; i < 3; ++i)
	{
		for (int j = 0; str[i][j] != '\0'; ++j)
		{
			if (str[i][j] == 'M')
			{
				return true;
			}
		}
	}

	return false;
}

int main()
{
	// Input
	char str[3][11];
	for (int i = 0; i < 3; ++i)
	{
		cin >> str[i];
	}

	// Sovle & Output
	cout << (IsExist(str) ? "M이 존재합니다." : "M이 존재하지 않습니다.");
	return 0;
}

 


#include <iostream>
using namespace std;

bool IsExist(char* board, char toFind)
{
	for (int i = 0; i < 4; ++i)
	{
		if (board[i] == toFind)
		{
			return true;
		}
	}

	return false;
}

int main()
{
	// Input
	char c[4] = { 'M','T','K','C' };
	char toFind;
	cin >> toFind;

	// Sovle & Output
	cout << (IsExist(c, toFind)?"발견":"미발견");
	return 0;
}

 


#include <iostream>
using namespace std;

int IsExist(char (*board)[3], char* toFind)
{
	int a = 0, b = 0;

	for (int i = 0; i < 2; ++i)
	{
		for (int j = 0; j < 3; ++j)
		{
			if (board[i][j] == toFind[0]) a = 1;
			if (board[i][j] == toFind[1]) b = 1;
		}
	}

	return a + b;
}

int main()
{
	// Input
	char c[2][3] = 
	{ {'G','K','T'},
	  {'P','A','C'} };

	char toFind[2];
	cin >> toFind[0];
	cin >> toFind[1];

	// Sovle
	int ret = IsExist(c, toFind);

	// Output
	if (ret == 2) cout << "대발견";
	if (ret == 1) cout << "발견";
	if (ret == 0) cout << "미발견";
	return 0;
}

 


#include <iostream>
using namespace std;


int main()
{
	// Input
	int arr[6];
	for (int i = 0; i < 6; ++i)
	{
		cin >> arr[i];
	}

	// Sovle & Output
	cout << arr[0] << ' ';
	for (int i = 0; i < 5; ++i)
	{
		arr[i + 1] = arr[i] + arr[i + 1];
		cout << arr[i + 1] << ' ';
	}

	return 0;
}

 


#include <iostream>
using namespace std;

void StrCpy(char* to, const char* from)
{
	int i;
	for (i = 0; from[i] != '\0'; ++i)
	{
		to[i] = from[i];
	}
	to[i] = '\0';

	return;
}

struct Wheel
{
	Wheel() = default;
	Wheel(int size, int radius)
		: mSize(size), mRadius(radius){};
	Wheel(const Wheel&) = default;
	~Wheel() = default;

	int mSize = 0;
	int mRadius = 0;
};

class Car
{
public:
	Car() = default;
	Car(const Car&) = default;
	~Car() = default;

	void PrintData() const
	{
		cout << mName << endl;
		cout << "Speed : " << mSpeed << "Km" << endl;
		cout << "Fuel : " << mFuel << "L" << endl;
		for (int i = 0; i < 4; ++i)
		{
			cout << "Wheel[" << i << "] : size " << mWheel[i].mSize << "inch radius " << mWheel[i].mRadius << "cm" << endl;
		}
	}

	void SetName(const char* str)
	{
		StrCpy(mName, str);
	}

	void SetWheel(const Wheel& w)
	{
		for (int i = 0; i < 4; ++i)
		{
			mWheel[i] = w;
		}
	}

	void SetSpeed(const int speed)
	{
		mSpeed = speed;
	}

	void SetFuel(const int fuel)
	{
		mFuel = fuel;
	}

private:
	char mName[100] = { 0 };
	Wheel mWheel[4];
	int mSpeed = 0;
	int mFuel = 0;
};



int main()
{
	// Input
	Car carA;
	carA.SetName("feraril");
	carA.SetSpeed(200);
	carA.SetFuel(100);
	carA.SetWheel(Wheel(5, 20));

	Car carB;
	carB.SetName("avante");
	carB.SetSpeed(100);
	carB.SetFuel(50);
	carB.SetWheel(Wheel(3, 20));


	// Output
	carA.PrintData();
	carB.PrintData();
	return 0;
}

||문제 이해

6
1  2
3  7  4
9  4  1  7
2  7  5  9  4

과 같은 형태의 삼각형이 주어지며 맨 위에서 한번에 한칸씩 내려갈 수 있다.

내려가는 방향은 바로 아래와 그 한칸 오른쪽으로 내려갈 수 있으며

모든 경로중 최대값을 구하라.


||해결 계획

이번 포스팅은 

문제를 보면서 "이렇게 생각을 전개해 나가면 좋겠다라고" 정리한 걸 중심으로 작성하겠다.


완전탐색

1. 답이 최대 합을 출력하라니까 일단 최대 합을 반환하는 함수형태를 만든다.

2. 좀 더 디테일하게 추가 해준다.

3. 재귀를 작성해 대강 재귀의 흐름을 만든다.

4. 좀 더 자세한 흐름을 만든다.

5. 재귀의 끝 부분인 기저 사례 작성

완전 탐색은 간단히 만들 수 있다.

 

하지만 변수 Cnt를 넣어 solve함수의 호출 횟수를 확인하자

이를 이용해 시간 복잡도를 계산해보면

n = 1 : O(1)

n = 2 : O(3)

n = 3 : O(7)

n = 4 : O(15)

n = 5 : O(31)

n = 6 : O(63)

으로 2^n - 1임을 알 수 있다.

 

이 문제에서 n의 최대 값은 100이므로 2^100은 주어진 시간 5초로도 불가능하다..

하지만 조금만 생각해도 중복되어 계산되고 있는 데이터가 있고

최적화 문제에도 잘 어울리는 DP를 사용해본다.

 

완전 탐색 + 메모리제이션

1. dp table에 무엇을 저장할지 생각한다. (완전탐색의 점화식과 비슷하다)

2. 메모리제이션 사용

위와 마찬가지로 카운팅을 해보면

n = 1 : O(1)

n = 2 : O(3)

n = 3 : O(7)

n = 4 : O(13)

n = 5 : O(21)

n = 6 : O(31)

로 O(n)보다 낮게 나오는걸 확인할 수 있다.


 

||문제 이해

2^20 * 2^20 의 판에서 각 칸은 흰,검으로 나타낼 수 있다.

 

1.모든 칸이 검은 색일 경우 = b 로 나타낸다.

2.모든 칸이 하얀 색일 경우 = w 로 나타낸다.

3.모든 칸이 통일되지 않으면 판을 4등분 각 부분을 1번부터 다시 적용한다.

 

합치는 순서는 [좌측상단 + 우측상단 + 좌측하단 + 우측하단] 이다.

 

놀랍게도 이 문제는 이렇게 압축을 한 값을 입력값으로 넘겨준다.

근데 그럼 그렇지, 이 입력값을 통해 원본판의 상하반전을 적용하여 재 압축한 결과를 출력해라.

 

처음예시에서 이게 무슨말인지 몰라 이해하는데 시간이 걸렸다.

저게 띄어써서 헷갈리는데 xxwwwb  xwxxbbbww  xxxwwbbbwwwwb  b로 보면 이해할 수 있다.

 


||해결 계획

이런 하나의 페이지를 나누어 문제를 해결 = 분할정복 유형

처음에 여러 생각을 했었는데 그중 하려던게

1. 원본을 구해놓고

2. 상하 반전을 적용하고

3. 다시 재압축 하는것이다.

 

근데 생각해보니까 굳이 상하반전을 할 필요없이 합치는 순서를 다르게 하면 상하반전 처럼 보이지 않을까 했다.

처음에 1 + 2 + 3 + 4 순서로 합했다면, 3 + 4 + 1 + 2 로 합치면 상하반전이 된다!

 

1. 이 아이디어로 재귀를 전개했다.

이렇게 하면 Solve가 상하반전한 문자열을 반환해 준다.( = 믿는다)

각 4부분에서도 상하반전한 문자열을 반환 한다고 한다.( = 믿는다)

그럼 결국에는 상하반전한 문자열을 반환할것이다.

 

 

2. 그리고 Base case를 생각해본다.

base case는 문제의 최소단계 즉, 계속 4등분되면서 한칸이 될 때다.

 


||계획 검증

이 예시로 처음으로 재귀의 마지막이 될때까지만 써보자

x는 이 그림의 압축 문자열인 xwxwbbbww 이다.

 

첫번째 재귀

1 : x[0] = 'x' + Solve()

 

두번째 재귀

1 : x[1] = 'w'

2 : x[2] = 'x' + Solve()

 

세번째 재귀

1 : x[3] = 'w'

2 : x[4] = 'b'

3 : x[5] = 'b'

4 : x[6] = 'b'

재조합 : bbwb반환

 

두번째 재귀

1 : 'w'

2 : x + bbwb

3 : x[7] = w

4 : x[8] = w

재조합 : wwwxbbwb 반환

 

첫번째 재귀 (제일 상단 레벨이라 한가지 밖에 없다!)

1 : 'x' + wwwxbbwb

재조합 : xwwwxbbwb 반환

 

출력 : xwwwxbbwb

 

그럼 상하반전과 맞는지 확인하자.

이대로 압축을 해보면

xwwwxbbwb로 정답이 나온다.

 

하지만 위의 설계와 다른부분이 있는데 첫번째 재귀가 그렇다.

이는 main에서 따로 작성해주면 될것같다! 


||구현

#include <iostream>
#include <queue>
#include <string>
using namespace std;

queue<char> q;

//현재 레벨 트리의 각 4분면 string을 구하고 재 조합하여 반환한다!
//불변식 : 이전레벨의 x는 없다.
string Solve(int lev)
{
	//base case : 1칸일때, 두 가지 경우가 있다.
	//1)최소레벨일때의 1칸, 2)최소레벨이 되지 않았을 때의 한칸
	if (lev == 0 || q.size() == 1)
	{
		string ret(1, q.front()); q.pop();
		return ret;
	}

	//1. 각 4분면의 문자열 저장
	string temp[4];
	for (int i = 0; i < 4; ++i)
	{
		temp[i] = q.front(); q.pop();
		if (temp[i] == "x") temp[i] += Solve(lev - 1);
	}

	//2. 재 합성후 반환
	return temp[2] + temp[3] + temp[0] + temp[1];
}


int main()
{
	int n; cin >> n;
	while (n--)
	{
		//1. Input value
		queue<char> tq; q = tq;
		string ts; cin >> ts;
		for (auto & d : ts) q.push(d);

		//2. Do solve
		string ret(1, q.front()); q.pop();
		if (ret == "x") ret += Solve(20);
		cout << ret << "\n";
	}
	return 0;
}

 


||되돌아보기

들어온 값을 앞에서 부터 빼고 싶어서 queue를 사용해 입력값을 받았다.

 

그리고 불변식부분을 생각안하니까 헷갈렸는데

불변식으로 확정지으니까 편하게 작성할 수 있었다.

 

당황했던거는 최소 트리 레벨을 0으로 해야하는데 1로 작성을 잘못했던 것

 

그리고 기억할것은 상하반전이라는 부분문제를 좀 더 효율적으로 생각해본것에 있다.

다른 문제에서도 부분문제를 좀 더 효율적이게 생각해야지!

 


||개선 코드

입력값을 큐를 쓰지 않고 순회하는 방법을 적용해봤다.

더 가벼워진 느낌이다.

string str;

//현재 레벨 트리의 각 4분면 string을 구하고 재 조합하여 반환한다!
//불변식 : 이전레벨의 x는 없다.
string Solve(string::iterator& it, int lev)
{
	//base case : 1칸일때, 두 가지 경우가 있다.
	//1)최소레벨일때의 1칸, 2)최소레벨이 되지 않았을 때의 한칸
	if (lev == 0 || it + 1 == str.end())
	{
		return string(1, *(it++));
	}

	//1. 각 4분면의 문자열 저장
	string temp[4];
	for (int i = 0; i < 4; ++i)
	{
		temp[i] = *(it++);
		if (temp[i] == "x")
		{
			temp[i] += Solve(it, lev - 1);
		}
	}

	//2. 재 합성후 반환
	return temp[2] + temp[3] + temp[0] + temp[1];
}

 

 

algospot.com :: QUADTREE

쿼드 트리 뒤집기 문제 정보 문제 대량의 좌표 데이터를 메모리 안에 압축해 저장하기 위해 사용하는 여러 기법 중 쿼드 트리(quad tree)란 것이 있습니다. 주어진 공간을 항상 4개로 분할해 재귀적

algospot.com

||문제 이해

포켓몬 도감 리스트가 주어지고

 

오박사가 번호를 물어보면 포켓몬의 이름을 출력,

오박사가 포켓몬의 이름을 물어보면 번호를 출력 하라.


||해결 계획

모두 직감적으로 느끼겠지만 이건 map이다.

 

하나 좀 고민했던게 있는데 map의 key는 하나다.

하지만 문제는 key를 번호, 이름 두가지로 묻고있다.

 

좀 고민하다가 나는 다 저장하기로 했다 ㅋㅋㅋ

map<string, string> 형식으로

{"1", "피카츄"}

{"피카츄","1"}

이렇게 번호도 string화 해서 map에 두번 저장하기로 했다.


||구현

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

unordered_map<string,string> dogam;

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

	int n, m; cin >> n >> m;
	for (int i = 1; i <= n; ++i)
	{
		string str; cin >> str;
		dogam.insert({str, to_string(i)});
		dogam.insert({to_string(i), str});
	}

	while (m--)
	{
		string str; cin >> str;
		cout << dogam.find(str)->second << '\n';
	}

	return 0;
}

||되돌아보기

다른 코드들도 보니 이런식으로 두번 저장하여 풀이하는듯 했다.

문제 읽는게 재미있었던 문제


||개선 코드

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

unordered_map<string,string> dogam;
string dogam2[1'000'005];

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

	int n, m; cin >> n >> m;
	for (int i = 1; i <= n; ++i)
	{
		string str; cin >> str;
		dogam.insert({ str, to_string(i) });
		dogam2[i] = str;
	}

	while (m--)
	{
		string str; cin >> str;
		if(isdigit(str[0]))
			cout << dogam2[stoi(str)]<< '\n';
		else
			cout << dogam.find(str)->second << '\n';
	}

	return 0;
}

 

map에서 숫자를 키로 받는 부분을 string배열을 만들어 따로 저장했다.

아무래도 탐색부분에 있어서 인덱스 참조는 O(1)이니까 훨씬 빨랐다.

 

그리고 이번에 알게된건데 isdigit은 int형을 반환한다. 반환 값이 0, 1뿐만 아니라 4 ,6이런게 나올수도 있다는 말이다.

그래서 if (isdigit(3) == true) 는 컴파일오류는 나지 않지만 원하는 결과가 나오지 않는다.

 

||문제 이해

자연수 m이 주어질 때, 행렬^m 을 구하라


||해결 계획

분할 정복을 사용한다.

 

1. 먼저 재귀의 초기형을 짜본다.

Pow는 행렬mat의 m거듭제곱을 반환해 준다! 라고 믿는다

 

 

2. 그리고 m이 짝수일 경우와 홀수일 경우가 다른데 일단 짝수로 생각해보자

행렬의 짝수 거듭제곱인 A^4를 풀어보면 이렇다.

A^4 = A * A * A * A

이 식은 행렬 A의 4거듭제곱이니 Pow(A, 4)와 같다!

 

곱셈은 자리를 변경해도, 어떤것부터 곱하던지 값은 항상 똑같기에 분할하기 편하게 반으로 나누어보자

A^4 = (A * A) * (A * A) = A^2 * A^2

이렇게 되면 저 하나의 A^2는 Pow(A,2)로도 가능하다!

즉, A^4 = Pow(A, 2) * Pow(A, 2) 이다.

그럼 최종의 최종은 Pow(A,4) = Pow(A, 2) * Pow(A, 2) 일 것이다.

 

하지만 이렇게 되면 사실 분할정복의 의미가 없다.

분할정복을 정리하면서 깨달은건데

큰 그림만 놓고보면 사실 일반 재귀와 분할정복의 재귀 호출 수는 똑같다.

하나의 뭉텅이가 재귀 한번인데 각 개수를 세보면 11개로 똑같다.

근데 이 문제에서의 분할 정복이 메리트가 있는 건

※모든 분할정복이 그렇다는 건 아님

분할했을 때 A뭉텅이, B뭉텅이가 나온다고 하면

A뭉텅이의 재귀 값으로 B뭉텅이를 재귀호출하지 않고 알 수 있어서가 아닐까 싶다.

암튼 지금까지 경험상은 그렇다.

 

그래서 분할정복을 사용하려면 코드는 이렇게 되어야한다.

 

 

3. 이제 짝수는 되었으니 간단한 홀수를 추가한다.

A^5 = A * A^4 인건 당연한 사실

즉, A * Pow(A, A-1)로 홀수거듭제곱을 알 수 있다.

 

 

4. Base case를 추가해주면 완성이다.


||구현

Matrix Pow(Matrix mat, int m)
{
	//base case, 최소의 부분문제이므로 m이 1일때이다.
	if (m == 1) return mat;

	//홀수일 때의 답 반환
	if (m & 0x1) return mat * Pow(mat, m - 1);

	//m이 짝수일 때의 답 반환
	Matrix half = Pow(mat, m / 2);
	return half * 2;
}

||되돌아보기

책에서는 홀수일때 다른 방법도 제시했는데

pow(A, m/2) * pow(A, m/2 + 1) 이다.

내가 작성한 짝수일때 pow(A, m/2) * pow(A, m/2)와 같은 경우다.

 

이것도 마찬가지로 재귀호출양이 훨씬 많다.

하지만 이 방식이 더 빠르게 답을 도출하는 방법이 있는데

바로 동적계획법(DP)이다.

 

근데 이말을 듣고 생각해보니까 분할하는 여러 방법이 있겠다 싶었다.

그래서 무조건 반이아닌 좀 더 효율적인 분할방법을 찾아보려고도 해야겠다!

 

dp는 아직 내가 자신있게 풀수있는건 아니여서 얼른 속도내서 뒷장에서 공부해야겠다 = ~ =

 

암튼 이번 문제를 정리하면서 분할정복이 왜 재귀와 차이가 나는지 깨달았다. 

이렇게 좀 논리적인 코드를 작성하려고 노력하다보니까 예전보다 재귀 작성하기가 쉬워졌고

앞으로도 항상 더 논리적인 코드를 짜려고 노력해야겠다.

||문제 이해

이차원 좌표 평면에서 시작된다. (x,y는 uv좌표계와 같은 방향을 가지고 있다.)

 

드래곤 커브는 속성을 기반으로 세대마다 길어진다.

여러 드래곤 커브가 있고 특정 세대까지 길어질 때,

1 * 1 정사각형의 꼭짓점이 드래곤 커브에 속하는 개수를 구하라.


||해결 계획

제일 먼저 든 생각은

모든 드래곤 커브의 꼭짓점을 구할 수 있는가? 였다,

그 꼭짓점들을 배열에 저장하고 싶었다.

 

그리고 간단한 판을 생각했다.

일단 드래곤 커브의 속성인지 뭔지는 중요하지 않고 빨간색이 드래곤 커브에 해당한 꼭짓점이라 하면

좌측 상단의 칸부터 정사각형이 될 수 있는지 검사하면 간단하다고 생각했다.

 

즉 부분 문제를 세가지로 나누었다.

1. 드래곤 커브 생성하기

2. 드래곤 커브에 속한 꼭짓점 저장하기

3. 판의 좌측 상단부터 정사각형 검사하기

 

코드를 대강 작성해보자

코드를 작성하다 보니까 굳이 100 * 100개의 꼭짓점을 검사해야싶나 했다.

그래서 드래곤 커브에 속한 꼭짓점만 검사하기로 했다.

 

여기서 set을 사용한 이유는 위에서 사각형 검사를 할때 중복으로 검사하지 않게 하고 싶어서 그렇다.


||계획 검증

 

일단 3 * 3판에 모든 드래곤 커브의 좌표가 빨간 좌표에 생겼다고 하자.

1.MakeDC( )를 하면 드래곤 커브를 생성하면서 저 빨간 점들이 저장될 것이다.

위의 예시라면 밑과 같이 저장이 될 것이다.

vector<pair<int,int>> vertices ={ {1, 1}, {1, 2}, {2, 1}, {2, 2}, {2, 3}, {3, 3} } 

 

 

2.CheckRect( )에서 vertices에 저장한 빨간 점을 모두 탐색한다.

└2.1 각 원소의 우측, 하단, 우측하단이 vertices에 저장되어있는지 확인하고 모두 있으면 카운트 한다.

(이 세가지만 확인함으로서 찾는 사각형이 중복되는걸 방어했다)

{1, 1} = {2, 1}, {1, 2}, {2, 2} = 카운트O

{1, 2} = {2, 2}, {1, 3}, {2, 3} = 카운트X

{2, 1} = {3, 1}, {2, 2}, {3, 2} = 카운트X

{2, 2} = {3, 2}, {2, 3}, {3, 3} = 카운트X

{2, 3} = {3, 3}, {2, 4}, {3, 4} = 카운트X

{3, 3} = {4, 3}, {3, 4}, {4, 4} = 카운트X

 

최종 1개로 답이 도출된다.

 

흠.. 일단 검증 같은걸 해보긴 했는데 이걸 검증이라고 할 수 있는지? 모르겠다


||구현

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

typedef pair<int, int> pii;
set<pii> vertices;

pii operator+(const pii & p1, const pii & p2)
{
	return { p1.first + p2.first, p1.second + p2.second };
}
pii operator-(const pii & p1, const pii & p2)
{
	return { p1.first - p2.first, p1.second - p2.second };
}
pii Rotate(pii & p)
{
	int temp = (p.second * -1);
	p.second = p.first;
	p.first = temp;
	return p;
}

//[0] : 시작 x좌표
//[1] : 시작 y좌표
//[2] : 시작 방향
//[3] : 세대
vector<vector<int>> dcProp;
vector<pii> tdc;

pii moving[4] = { {1, 0},{0, -1},{-1, 0}, {0, 1} };
void MakeDC()
{
	//1. 드래곤 커브를 개수만큼 생성한다.
	for (const auto & d : dcProp)
	{
		//1.1 0세대의 시작 좌표 저장
		pii s = { d[0], d[1] };
		tdc.push_back(s);
		//1.2 0세대의 끝점 계산, 좌표 기록
		tdc.push_back(s + moving[d[2]]);

		//1.3 세대가 성장하는가?
		if (0 < d[3])
		{
			//1.3.1	2세대이상부터, 입력 세대 만큼 커브 성장
			for (int i = 1; i <= d[3]; ++i)
			{
				pii e = tdc.back();
				int tempSize = tdc.size();
				//이전까지의 꼭짓점 회전 시켜 좌표 기록
				for (int j = tempSize - 2; 0 <= j ; --j)
				{
					pii t = tdc[j] - e;
					tdc.push_back(e + Rotate(t));
				}
			}
		}

		//최종 저장
		for (const auto & d : tdc)
		{
			vertices.insert(d);
		}
		tdc.resize(0);
	}
}

// (x, y) 형태의 좌표
pii preset[3] = { {1, 0}, {0, 1}, {1, 1} };
int ret = 0;
void CheckRect()
{
	//1. 각 꼭짓점 검사
	for (auto & d : vertices)
	{
		bool isRect = true;
		for (int i = 0; i < 3 && isRect; ++i)
		{
			if (vertices.find(d + preset[i]) == vertices.end()) isRect = false;
		}

		if (isRect == true) ++ret;
	}
}

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

	int c; cin >> c;
	dcProp.resize(c);
	for (int i = 0; i < c; ++i)
	{
		dcProp[i].resize(4);
		cin >> dcProp[i][0] >> dcProp[i][1] >> dcProp[i][2] >> dcProp[i][3];
	}
	MakeDC();
	CheckRect();

	cout << ret;
	return 0;
}

||되돌아보기

막힌 부분이 두 부분이 있었다.

 

1.문제의 조건을 잘못 이해

문제를 풀면서 점점 내가 문제를 이해 완벽히 이해하지 못한다고 생각이 들었다.

코드는 완성했는데 예시의 답과는 다르게 나오고,

조건이 틀렸는지 확인하고, 틀린 조건 수정하고 => 이 단계 반복이 좀 많았다;

 

문제 조금보고 이해했다고 깝치지말고 

다음에는 내가 이해한 것과 예시가 같은지도 제대로 비교해서 문제를 확실히 이해하고 넘어가야겠다😪

 

2.다양한 예시

내가 0세대에서 1세대로 넘어가는 예시만 생각하는 바람에

끝 좌표 이전의 모든 좌표를 회전시켜서 저장하지 못하고 시작 좌표만 회전시켜서 저장되었다.

 

너무 많은 예시는 아니더라도 최소 2개정도의 예시를 생각하자


||개선 코드

내 코드에서 일단 가볍게 개선 해봤다.

일단 회전 부분을 (y * -1, x)으로 간단히 했고

#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;

//[0] : start X //[1] : start Y //[2] : 시작 방향 //[3] : 세대
vector<vector<int>> dcProp;
set<pii> vertices;
vector<pii> tdc;

pii operator+(const pii & p1, const pii & p2)
{
	return { p1.first + p2.first, p1.second + p2.second };
}
pii operator-(const pii & p1, const pii & p2)
{
	return { p1.first - p2.first, p1.second - p2.second };
}

pii moveSet[4] = { {1, 0},{0, -1},{-1, 0}, {0, 1} };
void MakeDC()
{
	//1. 드래곤 커브를 개수만큼 생성한다.
	for (const auto & d : dcProp)
	{
		//1.1 0세대의 시작, 끝 좌표 저장
		pii s = { d[0], d[1] };
		tdc.push_back(s);
		tdc.push_back(s + moveSet[d[2]]);

		//1.2 1세대이상부터, 입력 세대 만큼 커브 성장
		for (int i = 1; i <= d[3]; ++i)
		{
			pii e = tdc.back();
			int tempSize = tdc.size();

			//1.2.1 끝점 이전의 꼭짓점들 회전 시켜 좌표 기록
			for (int j = tempSize - 2; 0 <= j ; --j)
			{
				pii t = tdc[j] - e;
				tdc.push_back(e + make_pair(t.second * -1, t.first));
			}
		}

		//최종 저장
		for (const auto & d : tdc)
		{
			vertices.insert(d);
		}
		tdc.resize(0);
	}
}

pii checkSet[3] = { {1, 0}, {0, 1}, {1, 1} };
int CheckRect()
{
	int ret = 0;
	//저장한 각 꼭짓점 검사
	for (auto & d : vertices)
	{
		bool isRect = true;
		for (int i = 0; i < 3 && isRect; ++i)
		{
			if (vertices.find(d + checkSet[i]) == vertices.end()) isRect = false;
		}
		if (isRect == true) ++ret;
	}
	return ret;
}

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

	//1. Input values
	int c; cin >> c;
	dcProp.resize(c);
	for (int i = 0; i < c; ++i)
	{
		dcProp[i].resize(4);
		cin >> dcProp[i][0] >> dcProp[i][1] >> dcProp[i][2] >> dcProp[i][3];
	}

	//2. Do calculate & Print result
	MakeDC();
	cout << CheckRect();
	return 0;
}

 

+ Recent posts