Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

15-miniron-v #56

Merged
merged 3 commits into from
Feb 18, 2024
Merged

15-miniron-v #56

merged 3 commits into from
Feb 18, 2024

Conversation

miniron-v
Copy link
Member

@miniron-v miniron-v commented Feb 14, 2024

πŸ”— 문제 링크

https://www.acmicpc.net/problem/1793
1793번: 타일링
λΆ„λ₯˜: DP, 큰 수 μ—°μ‚°

βœ”οΈ μ†Œμš”λœ μ‹œκ°„

50λΆ„

✨ μˆ˜λ„ μ½”λ“œ

image

1x2 타일, 2x1 타일, 2x2 타일을 μ΄μš©ν•΄ 2xN 칸을 μ±„μšΈ 수 μžˆλŠ” 경우의 수λ₯Ό κ΅¬ν•˜λŠ” 문제.

DP μžμ²΄λŠ” μ‰½κ²Œ λ– μ˜¬λ¦΄ 수 μžˆμ—ˆλ‹€.

μš°μ„  κ³΅ν†΅μ μœΌλ‘œ, 행은 2μ€„λ‘œ κ³ μ •μ΄λ―€λ‘œ, μ΄λŠ” 크게 신경쓰지 μ•Šκ³  열을 μ€‘μ‹¬μœΌλ‘œ μ§„ν–‰ν•œλ‹€.

1. 1개의 열을 μ±„μš°λŠ” 경우 (1x2)

μ΄λŠ” κ°„λ‹¨ν•˜λ‹€. 1x2 타일 ν•œ 가지 경우의 수만 μ‘΄μž¬ν•œλ‹€.

2. 2개의 열을 μ±„μš°λŠ” 경우 (2x2)

μ΄λŠ” 총 3가지

1. 1x2 2개
2. 2x1 2개
3. 2x2 1개

의 κ²½μš°κ°€ μ‘΄μž¬ν•œλ‹€.

ν•˜μ§€λ§Œ 이 쀑, 1x2 νƒ€μΌλ‘œ μ±„μš°λŠ” κ²½μš°λŠ” 1번 κ²½μš°μ™€ λ™μΌν•˜λ―€λ‘œ, μ—¬κΈ°μ„œ μ œμ™Έν•΄μ•Ό ν•œλ‹€.
그럼 총 2가지 경우의 μˆ˜κ°€ μ‘΄μž¬ν•œλ‹€.

πŸ“š 정리

2x17개의 타일을 μ±„μš°λŠ” 방법은

1. 16개λ₯Ό μ±„μš°λŠ” 경우의 수 x 1
2. 15개λ₯Ό μ±„μš°λŠ” 경우의 수 x 2

둜 정리할 수 μžˆλ‹€. (μ € 경우의 μˆ˜λ“€μ— μœ„μ— λ§ν•œ 1μ—΄, 2열을 μΆ”κ°€ν•œλ‹€ μƒκ°ν•΄λ³΄μž.)

이λ₯Ό κ°„λ‹¨νžˆ μ½”λ“œλ‘œ μž‘μ„±ν•˜λ©΄

#include <iostream>
#include <vector>

int main() {
	int n;
	while (std::cin >> n) {
		std::vector<int> dp(n + 1, 1);
		
		for (int i = 2; i <= n; ++i) {
			dp[i] = dp[i - 1] + 2 * dp[i - 2];
		}

		for (auto& i : dp) {
			std::cout << i << " ";
		}
	}
}

μ΄λ ‡κ²Œ 정리할 수 μžˆλ‹€.

😈 μ΄λ ‡κ²Œ λλ‚¬μœΌλ©΄ PR도 μ•ˆ 썼닀.

ν•˜μ§€λ§Œ 이 문제의 κ°€μž₯ 큰 ν›Όμ΄ν¬λŠ”, λ°”λ‘œ 이 뢀뢄이닀.

image

μ € κ·Ήμ•…λ¬΄λ„ν•œ μˆ«μžκ°€ λ³΄μ΄λŠ”κ°€? 저건... long longμœΌλ‘œλ„ λ„μ €νžˆ 컀버가 μ•ˆ λœλ‹€.

이 문제의 κ°€μž₯ μ€‘μš”ν•œ λΆ€λΆ„, λ°”λ‘œ 큰 수의 연산이닀. πŸ’£

😰 저건 μ–΄λ–»κ²Œ ν•˜λŠ”λ°?

C++μ—” μ†μ‰½κ²Œ 큰 수 연산을 ν•˜λŠ” 방법은 μ—†λ‹€. κ·Έλž˜μ„œ 직접 κ΅¬ν˜„ν•˜κΈ°λ‘œ μ •ν–ˆλ‹€.

κ°€μž₯ λ¨Όμ €, 큰 수λ₯Ό λ°›λŠ” 방법은 λ¬Έμžμ—΄λ‘œ λ°›λŠ” 것이닀.

그럼 λ¬Έμžμ—΄λ‘œ 받은 ν›„, λ¬Έμžμ—΄ 2개λ₯Ό μ •μˆ˜ λ§μ…ˆν•˜λŠ” 연산을 κ°œλ°œν•˜λ©΄ λ˜μ§€ μ•Šμ„κΉŒ? μƒκ°ν–ˆκ³ , 이에 μ°©μˆ˜ν–ˆλ‹€.

πŸ’€ λ¬Έμžμ—΄λ‘œ λ§μ…ˆν•΄λ³΄μž.

μš°μ„  두 λ¬Έμžμ—΄μ„ μž…λ ₯λ°›λŠ”λ‹€.

std::string string_add(std::string a, std::string b) {

}

우린 μ–΄μ°Œλλ“ , ν•œ μžλ¦¬μ”© λ–Όμ„œ μ—°μ‚°ν•΄μ•Ό ν•  것이닀. 그럼 λ‘˜ 쀑 κΈ΄ λ¬Έμžμ—΄μ„ a둜 고정해두면 νŽΈν•  것이닀.

// a > b, always.
if (a.size() < b.size()) {
	std::swap(a, b);
}

λ˜ν•œ, 이λ₯Ό μˆœνšŒν•˜λŠ” μΈλ±μŠ€κ°€ ν•„μš”ν•  것이닀.
ν•˜μ§€λ§Œ μ •μˆ˜μ˜ 연산은 맨 끝 μžλ¦¬λΆ€ν„° μ‹œμž‘ν•˜λ―€λ‘œ, a.size() - 1μ—μ„œ μ‹œμž‘ν•˜λŠ” i, b.size() - 1μ—μ„œ μ‹œμž‘ν•˜λŠ” j, 2κ°œκ°€ ν•„μš”ν•˜λ‹€.

이λ₯Ό ν†΅ν•©ν•˜κΈ° μœ„ν•΄, a와 bλ₯Ό λ’€μ§‘μ–΄μ„œ μ‹œμž‘ν•˜μž.

std::reverse(a.begin(), a.end());
std::reverse(b.begin(), b.end());

이러면 i = 0λΆ€ν„° b.size()κΉŒμ§€ λ°˜λ³΅λ¬Έμ„ 돌리면 λ˜λ‹ˆ, ν•œκ²° μˆ˜μ›”ν•΄μ‘Œλ‹€.

이제 ν•œ μžλ¦¬μ”© λ–Όμ„œ λ”ν•΄λ³΄μž.

int i = 0, carry = 0;
std::string sum = "";
for (; i < b.size(); ++i) {
	int num = (a[i] - '0') + (b[i] - '0') + carry;
	sum.push_back(num % 10 + '0');
	carry = num / 10;
}

ν•œ 자리 숫자 2개λ₯Ό λ”ν•˜λ©΄, μ΅œλŒ€ 2자리 μˆ˜κ°€ λ‚˜μ˜¨λ‹€. ν•˜μ§€λ§Œ sum 배열에 μΆ”κ°€ν•  수 μžˆλŠ” 건 κ·Έ 쀑 1의 자리뿐, 올림(carry)λŠ” λ‹€μŒ 칸에 써야 ν•œλ‹€.

κ·ΈλŸ¬λ‹ˆ char λ³€μˆ˜λ₯Ό int둜 λ³€ν™˜ν•œ ν›„ carry와 ν•¨κ»˜ λ§μ…ˆ, 10으둜 λ‚˜λˆˆ λ‚˜λ¨Έμ§€λ₯Ό λ‹€μ‹œ char둜 λ³€ν™˜ν•΄ sum에 μΆ”κ°€, 10으둜 λ‚˜λˆˆ λͺ«μ€ carry에 λ³΄κ΄€ν•œλ‹€.

그럼 반볡문이 λλ‚˜κ³ , 남은 carryλŠ” μ–΄λ–»κ²Œ ν•΄μ•Ό ν• κΉŒ?
이 경우 carryλŠ” a의 남은 μˆ˜λ“€μ— 더해져야 ν•œλ‹€. λ‹€ν–‰νžˆλ„ λ°˜λ³΅λ¬Έμ„ λΉ μ Έλ‚˜μ˜€λ©΄ i == b.size()μ΄λ―€λ‘œ, μ΄λŠ” carryλ₯Ό 더할 μžλ¦¬κ°€ 된
λ‹€.

a[i] += carry;

그리고 a의 남은 λ¬Έμžλ“€μ„ sum에 κ·ΈλŒ€λ‘œ 더해쀀닀.

while (i < a.size()) {
	sum += a[i++];
}

μ—¬κΈ°μ„œ λ¬Έμ œκ°€ ν•˜λ‚˜ λ°œμƒν–ˆλ‹€. 두 λ¬Έμžμ—΄μ˜ 길이가 κ°™λ‹€λ©΄ μ–΄λ–»κ²Œ μ²˜λ¦¬ν•΄μ•Ό ν• κΉŒ?

λ‚˜λŠ” κΈ°μ‘΄ μ½”λ“œλ“€μ„ μœ μ§€ν•˜κΈ° μœ„ν•΄, 두 λ¬Έμžμ—΄μ˜ 길이가 같을 λ•Œ (reverse ν›„) a의 끝에 0을 μΆ”κ°€ν•΄λ²„λ¦¬λŠ” 방식을 μ‚¬μš©ν–ˆλ‹€.
그럼 aλŠ” 항상 b보닀 κΈ΄ μƒνƒœκ°€ μœ μ§€λ˜λ©°, carryλŠ” λͺ¨λ“  경우 a에 빠짐없이 더해진닀.

이 경우 μ˜ˆμ™ΈλŠ”, a와 b의 길이가 κ°™κ³ , λ§ˆμ§€λ§‰ carryκ°€ 0인 경우. μ΄λ•Œ sum은 05724처럼, 맨 μ•ž μžλ¦¬μ— 0이 λΆ™μ–΄ 버린닀.

μ΄λŠ” sum을 λ‹€μ‹œ 뒀집기 전에, 끝 μžλ¦¬κ°€ 0이면 λ–Όλ²„λ¦¬λŠ”(pop_back) λ°©μ‹μœΌλ‘œ κ΅¬ν˜„ν–ˆλ‹€.

if (sum[--i] == '0') {
	sum.pop_back();
}

그리고 λ§Œλ“€μ–΄μ§„ sum을 뒀집어 좜λ ₯ν•˜λ©΄ 성곡.

이제 전체 μ½”λ“œλ‘œ μ‚΄νŽ΄λ³΄μž.

πŸ“š 전체 μ½”λ“œ

#include <iostream>
#include <vector>
#include <algorithm>

// λ¬Έμžμ—΄ λ§μ…ˆ
std::string string_add(std::string a, std::string b) {
	std::reverse(a.begin(), a.end());
	std::reverse(b.begin(), b.end());

	// a > b, always.
	if (a.size() < b.size()) {
		std::swap(a, b);
	}

	else if (a.size() == b.size()) {
		a += '0';
	}

	int i = 0, carry = 0;
	std::string sum = "";
	for (; i < b.size(); ++i) {
		int num = (a[i] - '0') + (b[i] - '0') + carry;
		sum.push_back(num % 10 + '0');
		carry = num / 10;
	}

	a[i] += carry;

	// aκ°€ 남은 경우
	while (i < a.size()) {
		sum += a[i++];
	}

	if (sum[--i] == '0') {
		sum.pop_back();
	}

	std::reverse(sum.begin(), sum.end());

	return sum;
}

int main() {
	int n;
	while (std::cin >> n) {
		std::vector<std::string> dp(n + 1, "1");

		for (int i = 2; i <= n; ++i) {
			dp[i] = string_add(dp[i - 1], dp[i - 2]);
			dp[i] = string_add(dp[i], dp[i - 2]);
		}

		std::cout << dp[n] << " ";
	}
}

πŸ“š μƒˆλ‘­κ²Œ μ•Œκ²Œλœ λ‚΄μš©

전체 사고 κ³Όμ •: https://autumncat.tistory.com/61

큰 수 연산은 처음 ν•΄λ΄€λŠ”λ°, κ·Έλž˜λ„ λ§μ…ˆμ€ λ‹¨μˆœν•΄μ„œ λ‹€ν–‰μ΄μ—ˆλ‹€. 이제 λ¬Έμ œλŠ” κ³±μ…ˆμΈλ°...
λ…Όλ¦¬νšŒλ‘œ λ•Œ 배운 방식이 잘 λ¨Ήν˜€μ„œ 닀행이닀. κΈ°μ–΅ μ•ˆ λ‚˜λ©΄ μ–΄μ…ˆλΈ”λ¦¬ μƒκ°ν•˜λ©΄μ„œ, 컴ꡬ μ§€μ‹μœΌλ‘œ κ΅¬ν˜„ν•΄μ•Όκ² λ‹€.

https://www.acmicpc.net/source/3116458
이런 방식도 λ°œκ²¬ν–ˆλŠ”λ°, μ΄ν•΄ν•˜κΈΈ ν¬κΈ°ν–ˆλ‹€. μ›λ¦¬λŠ” κ°™λ˜ μ΄μ§„μˆ˜λ₯Ό μ΄μš©ν–ˆλŠ”λ°, λ‚œ λ„μ €νžˆ 봐도봐도 λͺ¨λ₯΄κ² λ‹€... 이게 μ–΄λ–»κ²Œ λ§μ…ˆμ΄ λ˜λŠ” 건지.

https://codeforces.com/blog/entry/16380
λ‚˜μ€‘μ— 읽어보렀고 ν‚΅ν•΄λ‘” κΈ€. 큰 수의 연산을 λͺ¨λ‘ κ΅¬ν˜„ν•΄ μ •λ¦¬ν•΄λ‘μ—ˆλ‹€.. (아직 μ•ˆ λ΄„)

κ³¨λ“œ 타일 μ±„μš°κΈ°... κΉ° 수 μžˆμ„κΉŒ?

Copy link
Collaborator

@Redish03 Redish03 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

μ•„ γ…‹γ…‹ 문제 μ΄λ¦„λ§Œ 보고 음~ μƒλŒ€μ μœΌλ‘œ 이지 ν•œ 문제 ν•˜μ…¨λ„€ ν–ˆλŠ”λ°,,, 볡병이 μžˆμ—ˆκ΅°μš”

λ¬Έμžμ—΄ 닀루기 쒋은 것 κ°™μ•„μš” ν”„λ°κ²½μ§„λŒ€νšŒμ—μ„œλ„ 이런 문제 λ‚˜μ™”λ˜ κ±° 같은데 항상 μ €λŠ” 숫자λ₯Ό λ‹€λ£¨λŠ” λ¬Έμžμ—΄μ„ 건듀기 μ–΄λ ΅λ”λΌκ΅¬μš”,,,,

Comment on lines +19 to +25
int i = 0, carry = 0;
std::string sum = "";
for (; i < b.size(); ++i) {
int num = (a[i] - '0') + (b[i] - '0') + carry;
sum.push_back(num % 10 + '0');
carry = num / 10;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

가산기와 λ°˜κ°€μ‚°κΈ°λ₯Ό 직접 κ΅¬ν˜„ν•œ λŠλ‚Œμ΄λ„€μš”...

@9kyo-hwang
Copy link

input = open(0).readline

dp = [1] * 251
for i in range(2, 251):
    dp[i] = dp[i - 1] + 2 * dp[i - 2]
    
while True:
    try:
        print(dp[int(input())])
    except:
        break

혼자 κ·Έλƒ₯ 2 x n 타일링 λ¬Έμ œλž‘ λ˜‘κ°™μ΄ ν‘ΈλŠ” 파이썬...

Copy link
Collaborator

@2secondag 2secondag left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

μ˜€λžœλ§Œμ— λ³΄λŠ” λ―Όμ² λ‹˜ PR...!

큰 수의 연산을 ν•˜κΈ° μœ„ν•΄μ„œλŠ” μžλ£Œν˜•μ„ λŠ˜λ¦¬λŠ” 것이 μœ μΌν•œ 방법이라고 μƒκ°ν–ˆλŠ”λ° μžλ£Œν˜•μœΌλ‘œ ν•΄κ²°λ˜μ§€ μ•ŠλŠ” 경우라면 수λ₯Ό λ¬Έμžμ—΄λ‘œ 받은 ν›„ κ³„μ‚°ν•˜λŠ” 방법도 μžˆλ‹€λŠ” 것이 μƒˆλ‘œμ› λ˜ κ±° κ°™μŠ΅λ‹ˆλ‹€!

@Redish03
Copy link
Collaborator

μ˜€λžœλ§Œμ— λ³΄λŠ” λ―Όμ² λ‹˜ PR...!

큰 수의 연산을 ν•˜κΈ° μœ„ν•΄μ„œλŠ” μžλ£Œν˜•μ„ λŠ˜λ¦¬λŠ” 것이 μœ μΌν•œ 방법이라고 μƒκ°ν–ˆλŠ”λ° μžλ£Œν˜•μœΌλ‘œ ν•΄κ²°λ˜μ§€ μ•ŠλŠ” 경우라면 수λ₯Ό λ¬Έμžμ—΄λ‘œ 받은 ν›„ κ³„μ‚°ν•˜λŠ” 방법도 μžˆλ‹€λŠ” 것이 μƒˆλ‘œμ› λ˜ κ±° κ°™μŠ΅λ‹ˆλ‹€!

νŒŒμ΄μ¬μ€ 수 길이의 영ν–₯을 μ•ˆ λ°›μ•„μ„œ κ·ΈλŒ€λ‘œ 써도 λΌμš”! C, C++ 같이 μ΅œλŒ€ 길이가 μ •ν•΄μ Έ μžˆλŠ” 경우 λ―Όμ² λ‹˜μ²˜λŸΌ string을 μ΄μš©ν•˜λŠ” 방법이 μžˆμŠ΅λ‹ˆλ‹€. ꡳ이 μΆ”μ²œλ“œλ¦¬μ§€λŠ” μ•ŠλŠ”...λ°©λ²•μž…λ‹ˆλ‹€ ν•˜ν•˜
tmiμ§€λ§Œ cμ—μ„œ κ°€μž₯ 큰 수λ₯Ό ν‘œν˜„ ν•  수 μžˆλŠ” μžλ£Œν˜•μ€ unsigned long long int ν˜•μž…λ‹ˆλ‹€ ! 0μ—μ„œ 2^64 - 1 (18,446,744,073,709,551,615) 의 값을 λ‚˜νƒ€λ‚Έλ‹€κ³  ν•˜λ„€μš”

Copy link
Member

@bomik0221 bomik0221 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

κ³ μƒν•˜μ…¨μŠ΅λ‹ˆλ‹€!

PR 읽기 μ‹œμž‘ν–ˆμ„ λ•Œμ˜ λ‚˜ : 였~ λ‚˜λ„ ν’€ 수 μžˆκ² λŠ”λ°?
μ€‘λ°˜λΆ€ : 였~ λ‚˜λŠ” ν•  수 μ—†κ² λŠ”λ°?

큰 수λ₯Ό λ°›μ•„μ•Ό ν•  λ•Œ string으둜 λ°›λŠ” 게 λ‚«κ² λ‹€~ λΌλŠ” 생각은 ν–ˆμ§€λ§Œ, λ§μ…ˆμœΌλ‘œ κ°€λ‹ˆ 머리가 막 λ±…λ±… λ„λ„€μš”.
무슨 ꡬ쑰인지 μ΄ν•΄λŠ” ν–ˆμ§€λ§Œ, μ œκ°€ 받아듀이기엔 μ–΄λ €μš΄ 문제 κ°™μŠ΅λ‹ˆλ‹€... ν—ˆν—ˆ...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants