整数 を 8 で割ったあまりは、 の下三桁を 8 で割ったあまりに等しい!
問題概要
整数 が長さ の文字列として与えられる ( は '1'〜'9' のみで構成される)。
の各文字を並び替えてできる整数の中に、8 の倍数となるものが存在するかどうかを判定せよ。
制約
考えたこと
実は次のことが成り立つ。
- 2 の倍数かどうかの判定:下一桁が 2 の倍数かどうか
- 4 の倍数かどうかの判定:下二桁が 4 の倍数かどうか
- 8 の倍数かどうかの判定:下三桁が 8 の倍数かどうか
一般に の倍数であることは、下 桁が の倍数であることと同値となる。8 の倍数の場合について簡単に示しておこう。
なので、1000 が 8 で割り切れることに注意する。よって、任意の整数 に対して、 を 1000 で割った商を , あまりを とすると
となる。 はまさに「 の下三桁」である。このとき
となることがわかる。よって、 を 8 で割ったあまりは、 を 8 で割ったあまりに等しい!!
元の問題へ
以上のことを踏まえて、元の問題に戻ろう。つまり を上手に並び替えることで、下三桁を 8 の倍数にできるかどうかを判定する問題となる。
まず の場合は単純に全探索で OK。
のときは、次のように考えられる。
8 の倍数の下三桁としてありうるものをすべて考える。それらのうち、 から充足できるものがあるかどうかを判定する。
1000 未満の 8 の倍数は 124 個しかないので十分高速と言える。
コード
#include <bits/stdc++.h> using namespace std; bool solve(string &S) { if (S.size() <= 5) { sort(S.begin(), S.end()); do { int val = 0; for (auto c : S) val = val * 10 + (int)(c-'0'); if (val % 8 == 0) return true; } while (next_permutation(S.begin(), S.end())); return false; } vector<int> all(10, 0); for (const auto c : S) all[c-'0']++; for (int i = 0; i < 1000; i += 8) { vector<int> num(10, 0); int i2 = i; for (int iter = 0; iter < 3; ++iter) { num[i2 % 10]++; i2 /= 10; } bool ok = true; for (int v = 0; v < 10; ++v) if (num[v] > all[v]) ok = false; if (ok) return true; } return false; } int main() { string S; cin >> S; if (solve(S)) cout << "Yes" << endl; else cout << "No" << endl; }