예전에 틀렸던 부분 그대로 다시 틀렸다 ... 
 
#include <bits/stdc++.h>
using namespace std;

int n; // 기타 개수
string st;
vector<string> v;

int digit_sum(string &a){ // 문자열 중에 숫자만 뽑아서 합을 리턴
  int sum = 0;
  for(auto num : a) {
    if(isdigit(num)) sum += (num - '0');
  }
  return sum;
}

bool check(string &a, string &b){

  int alen = a.size(), blen = b.size(); // 문자열 길이 
  int asum = digit_sum(a), bsum = digit_sum(b); // 숫자합 

  // 길이가 다르면 길이끼리 비교
  if(alen != blen) return alen < blen;
  // 합이 다르면 합끼리 비교
  if(asum != bsum) return asum < bsum;
  // 사전순
  return a < b;
}

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

  cin >> n;

  while(n--){
    cin >> st;
    v.push_back(st);
  }
  sort(v.begin(), v.end(), check); // 비교함수로 정렬 
  for(auto s : v) cout << s << '\n';

  return 0;
}
 
문제의 정렬 조건은 3가지다. 
 
첫 째, 문자열 길이 →  size() 를 구해 비교
둘 째, 숫자 합 크기 → 문자를 isdigit() 으로 숫자인지 판별. 리턴은 bool
셋 째, 사전순 → a < b 아스키코드 오름차순이라서 간단.
 

아래는 틀린코드다.
bool check(string &a, string &b){

  int alen = a.size(), blen = b.size();

  if(alen < blen) return alen < blen;
  else if(alen == blen){ // 각각 숫자만 뽑아서 합 비교 
    int asum = 0, bsum = 0;
    for(int i = 0; i < alen; i++){ // 길이가 같으니까 인덱스 똑같이 사용 
      if(isdigit(a[i])) asum += (a[i] - '0');
      if(isdigit(b[i])) bsum += (b[i] - '0');
    }
    return asum < bsum;
  }else{ // 사전순
    return a < b;
  }
}
 
틀린 원인
길이 비교라는게, 길이가 달라야 비교하는 것이다. 
아래 처럼 <  로 조건을 넣으니까 틀렸다. 
[X] if(alen < blen) 
[O] if(alen != blen) 
 
숫자 합 비교도 마찬가지다. “달라야"비교한다. 
[X] if(asum < bsum) 
[O] if(asum != bsum) 
 
728x90

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

좋은 단어 백준 3986번 c++ (두번째 풀기)  (0) 2022.02.11
Hasing 백준 15829번 c++  (0) 2022.02.11
곱셈 백준 1629번 c++  (0) 2022.02.09
큰 수 A+B 백준 10757번 c++  (0) 2022.02.09
피보나치 수 4 백준 10826번 c++  (0) 2022.02.08
 
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

+ Recent posts