문제 링크 : www.acmicpc.net/problem/1086
이전에 포스팅했던 BOJ 1176 섞기와 동일한 접근법을 가진다.
어떠한 수들로 순열을 만들었을 때, 그 수들의 순서와 관계없이 우리는 이어붙인 수의 나머지만 알 면 된다.
X개의 수를 이어붙인 큰 수를 k로 나눈 나머지가 r이라면, 그 r에 새로운 수 하나를 붙인 다른 한 수에 대한 나머지가 곧 X + 1개의 수를 이어붙인 큰 수를 k로 나눈 나머지이기 때문이다.
즉 1, 3, 5번의 수를 (3, 1, 5)의 순서로 이어붙이거나 (5, 1 ,3)의 순서로 이어붙였을 때의 나머지가 r로 같다면, (1, 3, 5)번 수를 사용했을 때 나머지가 r인 경우의 수가 2개 존재하는 것과 같다.
총 수의 개수 N이 15로 매우 적으므로, 순열에 사용된 수들의 정보를 비트마스크로 나타낼 수 있다.
즉, 0개의 수를 사용했을 때의 dp[0000..0][0]에서 출발해 각 수들을 사용한 state를 갱신하여 결국 모든 수를 사용하게 되었을 때, 나머지가 0인 상태로 도달할 수 있는 경우의 수가 얼마인지만 세주면 된다.
※ (a * b) % k = ((a % k) * (b % k)) % k 임에 유의하자. 이는 새로운 수를 붙였을 때의 나머지를 구하는 데에 필요하다.
[코드]
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <cmath>
#include <memory.h>
using namespace std;
typedef pair<int, int> p;
typedef long long ll;
int n, k;
ll dp[1 << 16][100];
int remain_cache[16], remain_len[16];
int rcache[51];
vector<string> inputs;
int get_remain(string input, int k) {
int ret = 0;
for (int i = 0; i < input.size(); i++) {
ret = (ret * 10 + (input[i] - '0')) % k;
}
return ret;
}
ll get_dp(int state, int r) {
//만약 모든 수를 다 사용했을 때, 나머지가 0이 되는지 확인
if (state == (1 << n) - 1) return r == 0 ? 1 : 0;
ll& ret = dp[state][r];
if (ret != -1) return ret;
ret = 0;
for (int i = 0; i < n; i++) {
//i번째 비트가 현재 state에 포함되어 있는지 확인
if (state & (1 << i)) continue;
//i번째 비트를 추가한 상태
int nextstate = state | (1 << i);
//i + 1번째 수를 마지막으로 했을 때, 마지막 수의 나머지
int remain_last = remain_cache[i + 1];
//i + 1번째 수를 추가했을 때의 전체 나머지
int target = (r * rcache[remain_len[i + 1]] + remain_last) % k;
//다음 State에 대해 재귀실행
ret += get_dp(nextstate, target);
}
return ret;
}
void prev_action() {
//cache 저장
for (int i = 1; i <= n; i++) {
int remain = get_remain(inputs[i], k);
remain_cache[i] = remain;
remain_len[i] = inputs[i].size();
}
}
void init() {
string input;
cin >> n;
memset(dp, -1, sizeof(dp));
inputs.push_back("");
for (int i = 1; i <= n; i++) {
cin >> input;
inputs.push_back(input);
}
cin >> k;
rcache[0] = 1 % k;
for (int i = 1; i <= 50; i++) rcache[i] = (rcache[i - 1] * 10) % k;
}
ll get_gcd(ll a, ll b) {
while (b != 0) {
ll temp = a % b;
a = b;
b = temp;
}
return a;
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(NULL);
init();
prev_action();
ll answer = get_dp(0, 0);
if (answer == 0) {
printf("0/1\n");
return 0;
}
ll total = 1;
for (int i = 1; i <= n; i++) total *= i;
if (answer == total) {
printf("1/1\n");
return 0;
}
ll gcd = get_gcd(total, answer);
printf("%lld/%lld\n", answer / gcd, total / gcd);
//system("PAUSE");
return 0;
}
|
cs |
'알고리즘 > BOJ 문제풀이' 카테고리의 다른 글
[MST] BOJ 10423 전기가 부족해 (0) | 2021.01.19 |
---|---|
[Math] BOJ 1111 IQ Test (0) | 2021.01.18 |
[DFS] BOJ 17501 수식 트리 (0) | 2021.01.16 |
[LIS] BOJ 2631 줄세우기 (0) | 2021.01.14 |
[LIS] BOJ 11054 가장 긴 바이토닉 부분 수열 (0) | 2021.01.14 |