a의 b승을 계산해서 c로 나눈 나머지를 구해야 하는 문제다. 어려웠다. 
검색해보니까 분할정복으로 풀어야 하는 문제다. 

분할정복으로 a의 b승 구현하기 

a제곱 b는 아래와 같이 구현할 수 있다. 
int pow(int a, int b){
 if(b == 0) return 1; 
 if(b == 1) return a;
 if(b%2 == 0) { // 짝수 승이라면, 
  int temp = pow(a, b/2);
  return temp*temp;
 }
 return pow(a, b-1); // 홀수 승이면 1승 차이.
}
 
 
pow(a, b)함수로 2의 4승을 연산하려면 pow(2, 4)다. 
 
2의 0 승인 경우, 0승은 항상 1이다.
2의 1승인 경우, 1승은 항상 2이다. 
 
여기서 중요한 건, b가 짝수인 경우다. 
2의 4승 == ( 2의 2승 * 2의 2승 )
따라서 b가 짝수라면, b/2승 결과를 구해서 temp에 저장하고, temp를 제곱하면 된다. 

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

ll a, b, c;

ll rec(ll a, ll b){
  if(b == 0) return 1;
  if(b == 1) return a % c;

  long long temp = rec(a, b/2);
  if(b%2 == 0){ // b가 짝수라면,
    return (temp * temp) % c; // b/2승을 제곱하면 된다.
  }
  else{// b가 홀수라면, 1번 더 a를 곱해준다.
    return ((temp * temp) % c) * a % c;
  }
}

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

  cin >> a >> b >> c;
  cout << rec(a, b);
  return 0;
}
 
’나머지 구하기'를 제쳐두고 a의 b승을 하면, 숫자가 너무 커져서 long long 으로 연산이 불가능하다. 
 
따라서 ‘a를 c로 나눈 나머지'만 기억하면서 연산해야 한다. 
예를 들어, a의 1승은 a % c 가 되어야 한다. 
a의 2승은 (a % c) * (a % c) 가 되어야 한다. 
 
2의 4승 == ( 2의 2승 * 2의 2승 ) 임을 이용해서 분할정복으로 구현했다. 
 
짝수 b승을 계산할 때, 
b/2 승 * b/2 승을 연산하되, 마지막에는 반드시 c로 나눈 나머지를 저장하자. 
 

 

728x90

'알고리즘 > 백준' 카테고리의 다른 글

Hasing 백준 15829번 c++  (0) 2022.02.11
시리얼 번호 백준 1431번 c++  (0) 2022.02.10
큰 수 A+B 백준 10757번 c++  (0) 2022.02.09
피보나치 수 4 백준 10826번 c++  (0) 2022.02.08
피보나치 수 5 백준 10870번 c++  (0) 2022.02.08
 
long long, unsigned long long 으로도 못 담는 긴 숫자들이라 string 에 숫자들을 저장했다. 
연산하려면 자리수마다 숫자를 더해줘야 하니까, string을 vector<int>로 모두 변환했다. 
 
숫자 자리수가 다르더라도, 덧셈을 할 때는  두 숫자의 1의  자리 부터 하니까. 두 숫자의 길이가 맞아야 한다. 
짧은 숫자 vector에 0을 먼저 넣어둬서 a_vec과 b_vec의 길이를 맞춘다. 
 
vector의 맨 뒤가 1의 자리니까. 뒤에서부터 연산을 한다. 1의자리, 10의 자리, 100의 자리... 순이다. 
연산 결과는 answer 문자열에 한 자리 숫자를 붙인다. (숫자에 ‘0’을 더하면 문자 ‘0’이 된다. )
답 출력 직전에 answer 문자열을 뒤집으면, 정상 숫자로 출력할 수 있다. 
 
올림수 처리가 중요하다. 
예를 들어, 7 + 5 = 12 가 되니까. 올림수가 발생한다. 
carry 가 생기면, 1의 값을 갖게 했다. 그래서 다음 연산때 1을 더해주고, carry를 다시 0으로 돌려놓는다. 
 
#include <bits/stdc++.h>
using namespace std;

string a, b, answer;
vector<int> a_vec, b_vec;
int carry, temp_sum; // 올림수, 한 자리수 더함.

// 숫자 + '0' == 문자 
// 문자 - '0' == 숫자
int main(void){
  ios::sync_with_stdio(0);
  cin.tie(0);

  cin >> a >> b;

  if(a.size() < b.size()) swap(a, b);
  int diff_len = a.size() - b.size();

  // a, b 문자열의 문자를 하나씩 int 벡터로 넣는다.
  for(int i = 0; i < a.size() ; i++) a_vec.push_back(a[i]-'0');
  for(int i = 0; i < diff_len; i++) b_vec.push_back(0);
  for(int i = 0; i < b.size(); i++) b_vec.push_back(b[i]-'0');

  for(int i = a_vec.size()-1; i >= 0 ; i--){ // 뒤 부터 앞으로
    temp_sum = a_vec[i] + b_vec[i];

    if(carry == 1){ // 이전에 올림수가 있었다면, 1 더한다.
      temp_sum++;
      carry = 0;
    }
    if(temp_sum > 9){ 
      temp_sum %= 10;
      carry = 1; // 올림수 발생했으니까 1로 변경.
    }
    answer += (temp_sum + '0'); // 숫자를 문자열에 이어붙인다 
  }
  if(carry == 1) answer += '1'; // 올림수가 있다면, 1을 덧붙임
  reverse(answer.begin(), answer.end()); // 문자를 뒤집기 
  cout << answer;
  return 0;
}
 

참고 
728x90

'알고리즘 > 백준' 카테고리의 다른 글

시리얼 번호 백준 1431번 c++  (0) 2022.02.10
곱셈 백준 1629번 c++  (0) 2022.02.09
피보나치 수 4 백준 10826번 c++  (0) 2022.02.08
피보나치 수 5 백준 10870번 c++  (0) 2022.02.08
피보나치 수 백준 2747번 c++  (0) 2022.02.08
 
 
값이 길어져서 unsigned long long 으로도 커버가 안되는 문제다. 
string 끼리의 덧셈으로 풀어야 한다. 올림이 발생하면, 다음 자리수의 덧셈에 올림 수를 더해주는 연산이 필요하다. 
다른 분들의 풀이 포스팅을 읽고 이해했다. 다시 풀어 볼 문제. 
 
 
#include <bits/stdc++.h>
using namespace std;

int n, sum;
string a, b, tmp, fibo[10002];
vector<int> ans, num1, num2;

string big_num_sum(string s1, string s2){

  string s = "";

  // 더 긴 수를 s1에 저장한다.
  if(s1.size() < s2.size()) swap(s1, s2);

  // num1, num2 배열을 만든다.
  num1.push_back(0);
  num2.push_back(0);

  // s1이 더 긴 수니까. 그대로 num1 벡터를 채운다.
  for(int i = 0; i < s1.size(); i++)
    num1.push_back(s1[i] - '0');

  // s2가 비교적 짧은 수니까. s1, s2 길이 차이만큼 0을 num2 벡터에 채운다.
  for(int i = 0; i < s1.size() - s2.size(); i++)
    num2.push_back(0);

  for(int i = 0; i < s2.size(); i++)
    num2.push_back(s2[i] - '0');

  // num1, num2 벡터 맨 뒤부터 앞쪽으로 덧셈을 하면서 ans 벡터에 저장
  for(int i = s1.size(); i > 0; i--){
    sum = num1[i] + num2[i];
    if(sum > 9){
      num1[i-1] += 1; // 올림 수 발생했으니까 1을 더해준다.
      sum -= 10;
    }
    ans.push_back(sum);
  }

  // 맨 앞자리수에 올림수가 넘어왔다면,
  if(num1.front() != 0) s.push_back('1');

  // ans 벡터 원소 순서를 거꾸로.
  reverse(ans.begin(), ans.end());
  // ans 벡터의 숫자를 순차 출력하여 문자열 s에 이어붙인다. 
  for(auto n : ans){
    s.push_back(n+48); // 문자로 저장해야 하니까 48을 더해준다.  
  }
  num1.clear(); num2.clear(); ans.clear();
  return s;
}

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

  cin >> n;

  fibo[0] = "0";
  fibo[1] = "1";

  for(int i = 2; i <= n; i++){
    fibo[i] = big_num_sum(fibo[i-1], fibo[i-2]);
  }
  cout << fibo[n];

  return 0;
}
 
 

다시 푼 코드 링크

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

int input;
string a, b, answer; // 긴 숫자를 문자로 표현
vector<int> va, vb; // 연산을 위한 벡터

string fibo(string &a, string &b){
  string res = "";

  int diff_len = b.size()-a.size(); // 숫자 길이의 차이

  for(int i = 0; i < diff_len; i++) va.push_back(0);
  for(auto str : a) va.push_back(str - '0');
  for(auto str : b) vb.push_back(str - '0');

  int carry = 0;
  for(int i = vb.size()-1; i >= 0; i--){
    int temp_sum = va[i] + vb[i];

    if(carry == 1) { // 올림수가 있었다면, 더해주고. 다시 0 으로 돌려놓음
      temp_sum++;
      carry = 0;
    }
    if(temp_sum > 9){
      temp_sum %= 10;
      carry = 1;
    }
    res += (temp_sum + '0');
  }

  if(carry == 1) res += '1';
  reverse(res.begin(), res.end());
  va.clear(); vb.clear();

  return res;
}
// 숫자 + '0' == 문자
// 문자 - '0' == 숫자
int main(void){
  ios::sync_with_stdio(0);
  cin.tie(0);

  cin >> input;

  a = "0", b ="1";

  if(input == 0) cout << 0;
  else if(input == 1) cout << 1;
  else{
    for(int i = 2; i <= input; i++){
      answer = fibo(a, b);
      a = b;
      b = answer;
    }
    cout << b;
  }
  return 0;
}
728x90

'알고리즘 > 백준' 카테고리의 다른 글

곱셈 백준 1629번 c++  (0) 2022.02.09
큰 수 A+B 백준 10757번 c++  (0) 2022.02.09
피보나치 수 5 백준 10870번 c++  (0) 2022.02.08
피보나치 수 백준 2747번 c++  (0) 2022.02.08
피보나치 수 3 백준 2749번 c++  (0) 2022.02.08
n 번째 피보나치수를 구하는데, 20이하 번째만 구하면 된다. 
수의 크기가 작아서 재귀함수로 풀어도 가능하고. DP 메모이제이션으로 풀어도 통과한다. 
 
#include <bits/stdc++.h>
using namespace std;

long long D[22];
int n;

long long fibo(int num){
  if (num == 0) return 0;
  if (num == 1) return 1;
  return fibo(num-1) + fibo(num-2);
}

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

  cin >> n;
  cout << fibo(n);
  return 0;
}
 
#include <bits/stdc++.h>
using namespace std;

long long D[22];
int n;

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

  cin >> n;

  D[1] = D[2] = 1;
  for(int i = 3; i <= n; i++){
    D[i] = D[i-1] + D[i-2];
  }

  cout << D[n];
  return 0;
}

 

728x90

'알고리즘 > 백준' 카테고리의 다른 글

큰 수 A+B 백준 10757번 c++  (0) 2022.02.09
피보나치 수 4 백준 10826번 c++  (0) 2022.02.08
피보나치 수 백준 2747번 c++  (0) 2022.02.08
피보나치 수 3 백준 2749번 c++  (0) 2022.02.08
피사노 주기 백준 9471 c++  (0) 2022.02.08

문제링크 

 

DP 메모이제이션으로 풀 수 있었다. 

n번째 피보나치 수를  출력하면 된다. 

수의 크기 n이 크지않아서 long long 배열로 충분했다. 

 

 

"맞았습니다." 코드 

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

long long D[47];
int n;

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

  cin >> n;

  D[1] = D[2] = 1;
  for(int i = 3; i <= 45; i++){
    D[i] = D[i-1] + D[i-2];
  }

  cout << D[n];
  return 0;
}

 

728x90

백준 2749번 피보나치 수 3

 
 
백준 9471번 피사노주기 문제를 읽어보면, 피사노 주기의 내용을 설명하고 있다. 
주기의 길이가 p이면, 
n번째 피보나치 수를 m으로 나눈 나머지는 n%p번째 피보나치 수를 m으로 나눈 나머지와 같다. 
 
피보나치 수 3 문제에서 m은 10의 6승으로 주어졌다. 
n번째 피보나치 수를 10의 6승으로 나눈 나머지는 n%p번째 피보나치 수를 10의 6승으로 나눈 나머지와 같다. 
 
#include <bits/stdc++.h>
using namespace std;

long long n, cnt;
vector<int> v;

void pisano(int m){
  v.push_back(0); // 0번째 피보나치 수
  v.push_back(1); // 1번째 피보나치 수
  v.push_back(1); // 2번째 피보나치 수

  cnt = 2; // 3번째 피보나치 수를 구하기 위해 2번째와 1을 연산해야 한다.

  while(1){
    v.push_back((v[cnt] + v[cnt-1]) % m);
    cnt++;
    if(v[cnt] == 0 && v[cnt-1] == 1) break; // 주기를 구한 순간.
  }

}

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

  cin >> n;

  pisano(1000000);
  cout << v[n % cnt];

  return 0;
}
 



참고
728x90

'알고리즘 > 백준' 카테고리의 다른 글

피보나치 수 5 백준 10870번 c++  (0) 2022.02.08
피보나치 수 백준 2747번 c++  (0) 2022.02.08
피사노 주기 백준 9471 c++  (0) 2022.02.08
1로 만들기2 백준 12852 c++  (0) 2022.02.04
피보나치 수2 백준 2748 c++  (0) 2022.02.04

문제링크:  백준 9471 피사노 주기 

“ 피보나치 수열을 m으로 나눈 나머지가 P길이의 주기를 가진다.  “

 

 

피보나치 수열을 3으로 나누었을 때,  길이 8의 주기를 나타낸다. 
k(m)을 반복하는 부분수열의 길이라고 했을 때,  k(3) == 8 이다. 
주기의 길이가 p이면, n번째 피보나치 수를 mod으로 나눈 나머지는 n%p 번째 피보나치수를 mod로 나눈 나머지와 같다. 

주기를 이루는 숫자를 저장하는 벡터 v를 둔다고 생각해보자 (mod으로 나눈 나머지를 저장한다)

나누는 숫자 mod은 2 이상의 숫자가 주어진다

mod가 2일 때, 피보나치 수1을 2로 나누면 나머지가 1이다.  v[0] = 0, v[1] = 1, v[2] = 1 가 된다. 

 

주기의 앞 부분은 확실히 0, 1 순으로 시작하니까. 0, 1 이 연속으로 나오면 주기가 반복되는 지점임을 알 수 있다. 

그래서 벡터 v는 필요 없고 숫자 m1, m2만 필요하다. 


#include <bits/stdc++.h>
#define endp '\n';
using namespace std;

int p, n, m;

int pisano(int mod){

  int cnt = 0; // cnt 번째 피보나치 수
  int m1 = 0, m2 = 1; // 0과 1이 순서대로 나오면, 주기 발견.
  int tempsum = 0;

  while(1){
    tempsum = m1 + m2;
    m1 = m2;
    m2 = tempsum % mod;
    cnt++;
    if(m1 == 0 && m2 == 1) break;
  }

  return cnt;
}

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

  cin >> p;

  for(int i = 1; i <= p; i++){
    cin >> n >> m;
    cout << n << ' ' << pisano(m) << endp;
  }

  return 0;
}

 
 
 

참고
728x90

'알고리즘 > 백준' 카테고리의 다른 글

피보나치 수 백준 2747번 c++  (0) 2022.02.08
피보나치 수 3 백준 2749번 c++  (0) 2022.02.08
1로 만들기2 백준 12852 c++  (0) 2022.02.04
피보나치 수2 백준 2748 c++  (0) 2022.02.04
카드 백준 11652 c++  (0) 2022.02.04

+ Recent posts