일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- 재귀
- react
- 이벤트 생명주기
- 정적타입언어
- 구현
- 동적타입언어
- 누적합
- 이분탐색
- SW EA
- 브루트포스
- 값복사
- 리액트
- 렌더링 최적화
- next14
- vscode
- webpack
- 즉시실행함수
- 두 포인터
- webpack5
- 마진 상쇄
- 수학
- BFS
- 컴포넌트 생명주기
- 슬라이딩 윈도우
- 분할정복
- 백준
- 공백찾기
- react18
- 레퍼런스복사
- 레이아웃 스래싱
- Today
- Total
D.JOUNG
백준 문제풀이 : (1790) 수 이어 쓰기 2 - c/c++ 본문
문제 : https://www.acmicpc.net/problem/1790
1790번: 수 이어 쓰기 2
첫째 줄에 N(1 ≤ N ≤ 100,000,000)과, k(1 ≤ k ≤ 1,000,000,000)가 주어진다. N과 k 사이에는 공백이 하나 이상 있다.
www.acmicpc.net
시간 제한 | 메모리 제한 | 난이도 | 알고리즘 분류 |
2초 | 64 MB | Gold 5 | 수학 / 구현 |
문제
풀이
수가 십의 제곱 단위로 커지는 구간 마다 수와 숫자의 개수는 다음과 같다.
한 자릿수 범위 (1~9)
수 : 9개 / 숫자 : 9개
두 자릿수 범위 (10~99)
수 : 90개 / 숫자 : 180개
세 자릿수 범위 (100~999)
수 : 900개 / 숫자 : 2700개
이 규칙을 다음과 같은 공식으로 정리할 수 있다.
수의 개수 : 9 * 10의 (자릿수-1) 승
숫자의 개수 : 9 * 자릿수 * 10의 (자릿수-1) 승
문제에서는 k번째 숫자를 구하는게 목적이므로, 루프를 돌려 k에서 각 자릿수 범위 마다의 숫자 개수를 차감해준다.
차감이 끝난 후의 k를 kk라고 명명한다. 루프가 끝난 후, kk는 계산한 자릿수 범위 내에서의 위치가 된다.
따라서 해당 자릿수 범위 내의 최솟값에 "(kk-1)/자릿수" 값을 더해주면 k를 가진 수가 무엇인 지 알 수 있다.
이 수를 num이라 했을 때, num에서 몇 번째 숫자가 정답인 지는 % 연산자를 활용해 구한다.
ex) n=20 , k=23
루프를 돌려 k에서 자릿수 범위마다의 숫자 개수를 차감해준다.
loop1) 23-9 = 14
loop2) 14-180 = (음수 값이 됨. 따라서 반복 종료)
위 계산 결과 k의 자릿수는 2이다. 그리고 kk= 14 인 상태에서 반복이 종료되었다.
따라서 k번째 숫자를 가진 수(num)는 "10의 0(자릿수-1)승 + (kk-1)/자릿수" 가 되어, 10+6 = 16이 된다.
num에서 몇 번째 숫자가 정답인 지는 (kk-1)%자릿수 하여 알 수 있다. 하지만 수의 숫자를 0, 1, 2, 3.. 인덱스 순으로 접근해야 하므로 < num의 "자릿수-(kk-1)%자릿수"번째 숫자> 가 정답이 된다.
코드 작성
#include <iostream>
using namespace std;
long long powNum(int num, int quot) {
if (quot==0) return 1;
return num*powNum(num, quot-1);
}
long long getResult(int num, int cipher) { //숫자에서 자리에 있는 수 가져오기
return num/powNum(10, cipher-1)%10;
}
int main() {
long long n, k;
scanf("%lld %lld", &n, &k);
long long kk=0; //루프 문 내에서 변경되는 k의 값을 저장한다.
long long cc=0; //루프 문 내에서 변경되는 c(반복횟수=자릿수)값을 저장한다.
for (long long c=1 ; k>0 ; c++) { //k의 값이 음수가 되면 종료.
kk=k; //k의 음수 이전 값을 구해야 하므로 마이너스 연산 전 kk에 따로 저장한다.
cc=c;
k-=(9*c*powNum(10, c-1)); // 9, 180, 2700 순으로 마이너스 된다.
}
long long num=powNum(10, cc-1)+(kk-1)/cc; // k번째 숫자를 갖고 있는 수를 구한다.
long long result = getResult(num, cc-(kk-1)%cc); //num의 앞에서 몇 번째 수가 정답인지 계산
if (num>n) result=-1; //num이 n보다 크다면 범위 초과이니 -1 출력
printf("%lld", result);
}
END.
'알고리즘 > 백준' 카테고리의 다른 글
백준 문제풀이 : (10986) 나머지 합 - c/c++ (0) | 2023.11.28 |
---|---|
백준 문제풀이 : (6064) 카잉 달력 - Python (3) | 2023.11.23 |
백준 문제풀이 : (2022) 사다리 - c/c++ (0) | 2023.11.21 |
백준 문제풀이 : (1105) 팔 - c/c++ (0) | 2023.11.18 |
백준 문제풀이 : (15973) 두 박스 - c/c++ (0) | 2023.11.17 |