C - Giant Domino Editorial by en_translator
Consider placing dominoes from the left.
If domino \(i\) is placed at the end, the next domino that we can place is the one that satisfies \(S_j \leq 2 S_i\). The larger \(S_i\) is, the more this condition is relaxed. Thus, it is better to place as large a domino as possible.
Therefore, the following greedy algorithm is valid.
- Place domino \(1\) at the left end.
- Repeat the following operation.
- Let domino \(i\) be the rightmost domino placed.
- If \(2 S_i \geq S_N\), place domino \(N\) at the end, terminate the procedure, and report the number of dominoes placed so far.
- Otherwise (i.e. if \(2S_i \lt S_N\)), find the maximum \(j\) such that \(2S_i \geq S_j\).
- If no \(j\) satisfies the condition, or \(S_i \geq S_j\), then we can never place a domino larger than \(S_i\), so we cannot place domino \(N\). Print \(-1\) and terminate the algorithm.
- Otherwise, place domino \(j\) to the right of domino \(i\).
- Print the number of dominoes placed so far when the procedure ends, and terminate the algorithm.
This greedy algorithm finishes after \(\mathrm{O}(K N)\) times, where \(K\) is the number of times the domino was placed. Also, one can prove that \(K\) is at most \(60\) (\( = 2 \lceil \log_2 \max(A) \rceil\)), although the proof is not very easy.
(Proof) Let \(s_1, s_2, \dots, s_K\) be the sequence of the sizes of dominoes placed. If \(s_1 \times 2 \geq s_3\), it means \(s_3\) can be placed without placing \(s_2\), violating the algorithm. Therefore, \(s_1 \times 2 \lt s_3\), so \(s_3\) is greater than two times \(s_1\).
Similarly, one can prove that \(s_{i+2}\) is at least twice as large as \(s_i\). If you assume \(K \geq 61\), then \(s_{61}\) turns out to be \(2^{30}=1073741824\) times as large as \(s_1\), but it violates the input constraints \(A_i \leq 10^9\). Hence, it has been prove that \(K \leq 60\).
Therefore, the problem has been solved in \(\mathrm{O}(N \log \max(A))\) steps, which is fast enough.
- Sample code (C++)
#include <iostream> #include <vector> using namespace std; void solve() { int N; cin >> N; vector<int> A(N); for (auto& a : A) cin >> a; vector<int> used(N); int ans = 1, last = 0; while (true) { if (A[last] * 2 >= A[N - 1]) { ans += 1; break; } int nxt = -1; for (int i = 1; i <= N - 1; i++) { if (used[i]) continue; if (A[last] * 2 >= A[i]) { if (nxt != -1 && A[nxt] >= A[i]) continue; nxt = i; } } if (nxt == -1 || A[nxt] <= A[last]) { cout << -1 << endl; return; } ans++, last = nxt, used[nxt] = 1; } cout << ans << endl; } int main() { int T; cin >> T; while (T--) solve(); } posted:
last update: