[질문]
백준 1664 도움!
알고리즘개초보(dntjwkd00)
2019-03-28 01:54
추천 1
https://www.acmicpc.net/problem/1664
일단 올려놓고 계속 풀긴할건데, 여러분들 아이디어가 궁금해서요.
저는 c의 유무에 따라서 풀고있는데
c가 없을 때는 구할 수 있는뎅
문제가 c가 있을때네요.
c가 없을 때 풀듯이하면 한번씩 다 검사를 해줘야하기때문에, 무조건 타임아웃이 나오고
지금 생각한거론
c가 나올 수 있는 값 = (19*X) + C이니까, D~A까지의 최댓값 내에서 반복해서 값을 찾는건데..
너무 복잡하고, 아프네요 지금 1시간 30분째 생각하고있는데 ㅋㅋㅋㅋ
문제
내일이면 대한민국에 새로운 대통령이 취임하게 된다. 새로운 정부는, 아래와 같이 19자리로 된 새로운 주민등록번호 체계를 도입한다고 한다.
DDMMYYYYAAAAAAAAAAC
YYYY는 생년, MM은 생월, DD는 생일을 의미한다. 생년은 0001 이상 9999 이하의 수가 되며, MM은 01 이상 12 이하, DD는 01 이상 31 이하이다. 1, 3, 5, 7, 8, 10, 12월은 31일까지, 4, 6, 9, 11월은 30일까지이며, 2월은 평년은 28일까지, 윤년은 29일까지이다. 윤년이란 YYYY가 (1) 100의 배수를 제외한 4의 배수이거나 (2) 400의 배수인 경우가 해당된다.
A로 된 10자리는 어떤 숫자라도 올 수 있다. 마지막 자리인 C는 CONTROL-DIGIT으로, 아래와 같은 알고리즘에 의해 생성된다.
- C를 제외한 주민등록번호상의 18자리의 수를 순서대로 Z1, Z2, …, Z18이라고 하자.
- S = (10×Z1 + 9×Z2 + 8×Z3 + … + 2×Z9 + 10×Z10 + 9×Z11 + 8×Z12 + … + 2×Z18)
- S가 9 이하이면 C=S, 아니면 C = 19 - S
이러한 새로운 주민등록번호 체계상의 한 주민등록번호의 일부 숫자가 지워져 있다. 위의 조건을 만족시키는 가능한 모든 주민등록번호의 경우의 수를 세는 프로그램을 작성하시오.
입력
첫째 줄에 19자리의 주민등록번호가 주어진다. 숫자 또는 X로 주어지게 되는데 X는 숫자가 지워졌음을 의미한다.
출력
첫째 줄에 조건을 만족시키는 가능한 모든 주민등록번호의 경우의 수를 출력한다. 답은 항상 2^63보다 작다.
예제 입력 1 <button type="button" class="btn btn-link copy-button" data-clipboard-target="#sample-input-1" style="line-height: 1.42857; font-size: 14px; cursor: pointer; color: rgb(66, 139, 202); border-width: 1px; border-style: solid; border-color: transparent; text-align: center; border-radius: 0px; font-style: inherit; font-variant: inherit; font-stretch: inherit; overflow: visible; white-space: nowrap; vertical-align: middle; user-select: none; background-image: none; box-shadow: none; font-family: "Open Sans", "Apple SD Gothic Neo", "Noto Sans CJK KR", "Noto Sans KR", 나눔바른고딕, 나눔고딕, 맑은고딕, "Helvetica Neue", Helvetica, Arial, sans-serif !important; outline: 0px !important;">복사</button>
XX0220051234567890X
예제 출력 1 <button type="button" class="btn btn-link copy-button" data-clipboard-target="#sample-output-1" style="line-height: 1.42857; font-size: 14px; cursor: pointer; color: rgb(66, 139, 202); border-width: 1px; border-style: solid; border-color: transparent; text-align: center; border-radius: 0px; font-style: inherit; font-variant: inherit; font-stretch: inherit; overflow: visible; white-space: nowrap; vertical-align: middle; user-select: none; background-image: none; box-shadow: none; font-family: "Open Sans", "Apple SD Gothic Neo", "Noto Sans CJK KR", "Noto Sans KR", 나눔바른고딕, 나눔고딕, 맑은고딕, "Helvetica Neue", Helvetica, Arial, sans-serif !important; outline: 0px !important;">복사</button>
28
우선 A 10자리에서 1..i 부분의 합이 j인 경우를 dp로 계산해두고, 그걸 활용해서 생년월일은 걍 브루트포스 돌리면 뚝딱일 것 같은 느낌
뭔느낌인질 모르겠는뎅 C의 값이 정해져있고, 브루트포스로 하면 단순 계산 경우의 수만 따져봐도 9998 * 12 * 30 * (A안에있는 XX의 갯수 * 10)임.. 이걸 다 계산했을 때, C값이 나와야하는데, dp로하는것도 월마다 윤년인지 평년인지에 따라서 갈 수 있는 경우의수가 변하다보니까 dp를 활용할 방법을 모르겠음 ㅇㅇ.. 이 문제는 C의 유무가 큰 것 같긴한데, 있든 없든 년 - 월 - 일의 데이터 연계가 중요한것같음...ㅠㅠ.. 어렵네 이거..
D[i][j] = sum { W[8+i] * Z[8+i] : i=1..10 } mod 19가 j인 경우의 수 = Z[i]='X'이면 sum { D[i-1][j-W[8+i] * k mod 19] : k=0..9 }, 아니면 D[i-1][j-W[8+i] * Z[8+i] mod 19]임. 만약 날짜(Z[1..8])가 고정되어있고 S=sum{W[k]*Z[k] : k=1..8}라고 할 때, 경우의 수는 D[10][C-S mod 19] + D[10][19-S-C mod 19]이고, 따라서 가능한 날짜를 모두 돌아보면서 합을 구해주면 됨. W[i]는 S를 구할 때 Z[i]에 곱하는 수임
세부 구현을 설명해서 좀 더럽게 보이는데... 그냥 A 부분을 DP로 전처리해두면, 각 날짜별로 만들어질 수 있는 주민등록번호의 수를 O(1)에 구할 수 있단 얘기임. 그러니까 총 날짜의 수(대략 9999*365)만큼만 계산하면 된다는 얘기 ㅇㅇ