AOJ - ZIMBABWE 웨브바짐
이거 예전에 종만북 볼 때 정말 어려웠던 문젠데, 다시 푸니 풀렸다.
약간 Digit DP라고 부르는 류의 연습 문제정도가 되는 수준이고, 추후에 Digit DP에 대해서도 이 블로그에도 글을 작성할 예정이다.
정도로 두고,
- available: 지금까지 사용한 숫자를 비트로 표현한 것
- modular: 으로 나눈 나머지를 계속 가져가는 것
- less: 이미 원본 숫자보다 작아졌는지를 검사하는 플래그
dp를 아름답게 돌리면 된다.
주의할 점은 수가 중복되지 않게 가능한 숫자들을 모두 정렬해두고 이거나 마지막으로 현재 사용한 숫자가 지금 사용해볼 숫자보다 작을때만 자식 재귀함수를 호출해주는 것이다.
const ll mod = 1e9 + 7;
inline ll md(ll x) { return md(mod, x); }
int ten[17];
// 지금 계란 가격 e
// 최종 계란 가격은 m으로 나뉘어진다.
// 최종 가격 계란은 0으로 시작할 수 있다.
// 최종 계란 가격은 e보다 작아야 한다.
int e, m, e_size;
string e_string, e_sorted;
inline int get_original_digit(int i) {
return e_string[i] - '0';
}
inline int get_sorted_digit(int i) {
return e_sorted[i] - '0';
}
int dp[1 << 16][22][2];
int fn(int available, int r, int less) {
int cnt = __builtin_popcountll(available);
if (cnt == 0) return r == 0 && less;
int &ret = dp[available][r][less];
if (~ret) return ret;
ret = 0;
int last_used = -1;
for (int i = 0; i < e_size; i++) {
int original_digit = get_original_digit(e_size - cnt);
int cur_digit = get_sorted_digit(i);
if (cur_digit > last_used && ((1 << i) & available)) {
if (!less && cur_digit > original_digit) continue;
last_used = cur_digit;
ret = md(ret + fn(available & ~(1 << i),
md(m, r + ten[cnt - 1] * cur_digit),
less || (cur_digit < original_digit))
);
}
}
return ret;
}
void solve() {
memset(dp, -1, sizeof dp);
cin >> e >> m;
ten[0] = 1;
for (int i = 1; i <= 16; i++) ten[i] = ten[i - 1] * 10;
e_string = to_string(e);
e_sorted = e_string;
sort(all(e_sorted));
e_size = sz(to_string(e));
cout << fn((1 << e_size) - 1, 0, 0) << endl;
}
Comments