[AtCoder] ABC 184 F – Programming Contest
問題
方針
半分全列挙
\( n = \min(20, N) \) として、問題を \( n \) と \( N – n \) 個に分けて考えます。前半の問題の集合を \( A \) とし、後半を \( B \) とします。\( A \) の \( n \) 個の問題から選んで \( T \) 分以下となる時間の総和は、\( 2^n \) 通りあることが分かります。これは、ビット全探索を用いて列挙できます。
\( A \)、\( B \) のそれぞれの時間の総和を昇順に並び替え、\( A \) からある時間 \( x \) を選んだとき、\( B \) の時間の総和 \( y \) として、\( x + y \leq T \) を満たす最大の \( y \) を探索します。これは、二分探索で実現できます。
分枝限定法
この問題はいわゆる 0-1 ナップサック問題において、価値と重さが等しいものであると解釈できるのできます。したがって、分枝限定法によって解くことができます。
コード
半分全列挙
#include <bits/stdc++.h> using namespace std; typedef long long ll; int main() { int N; cin >> N; ll T, A[N]; cin >> T; for (int i = 0; i < N; i++) { cin >> A[i]; } int n = min(20, N); int m = N - n; vector<ll> v1, v2; v1.push_back(0); v2.push_back(0); int k1 = 1<<n; int k2 = 1<<m; for (int i = 1; i < k1; i++) { ll t = 0; for (int j = 0; j < n; j++) { if (((1<<j) & i) != 0) { t += A[j]; } } if (t <= T) { v1.push_back(t); } } for (int i = 1; i < k2; i++) { ll t = 0; for (int j = n; j < N; j++) { if (((1<<(j - n)) & i) != 0) { t += A[j]; } } if (t <= T) { v2.push_back(t); } } sort(v2.begin(), v2.end()); ll ans = 0; for (ll i : v1) { int l = -1; int r = v2.size(); while (r - l > 1) { int m = l + (r - l) / 2; if (i + v2[m] <= T) { l = m; } else { r = m; } } if (ans < i + v2[l]) { ans = i + v2[l]; } } cout << ans << "\n"; return 0; }
分枝限定法
#include <bits/stdc++.h> using namespace std; typedef long long ll; int N; ll T; ll best = 0; ll A[40]; ll s_A[41]{}; // A_l + ... + A_{r - 1} ll sum_v(int l, int r) { return s_A[r] - s_A[l]; } void dfs(int n, ll t) { if (t <= T) { best = max(best, t); } if (n == N || t >= T) return; ll _t = t; int idx = -1; for (int i = n; i < N; i++) { if (_t + A[i] <= T) { _t += A[i]; } else { idx = i; } } best = max(best , _t); if (idx == -1) { return; } else { _t = _t * A[idx] + (T - _t) * A[idx]; if (_t <= best * A[idx]) return; } if (_t == T) { best = T; return; } dfs(n + 1, t + A[n]); dfs(n + 1, t); } int main() { cin >> N >> T; for (int i = 0; i < N; i++) { cin >> A[i]; } sort(A, A + N, greater<ll>()); dfs(0, 0); cout << best << "\n"; return 0; }
ディスカッション
コメント一覧
まだ、コメントがありません