本番なんとかブザービートが決まった!
問題概要
赤いベリーと青いベリーとがある。
組のビュッフェがあり、それぞれ赤いベリーが 個、青いベリーが 個入っている。
これらをカゴに詰めていきたい。1 つのカゴにはちょうど 個のベリーを入れたい。ただし、1 つのカゴには
- 同じビュッフェのベリー
- 同じ色のベリー
のどちらかの構成でなければならない。最多で何個のカゴが作れるか?
制約
考えたこと
直感的には、
- 赤いベリーだけでできるだけ 個ずつ詰める
- 青いベリーだけでできるだけ 個ずつ詰める
という風にした場合、ほとんどのベリーを詰めることができて、このとき残るのは
- の総和を で割ったあまり 個の赤いベリー
- の総和を で割ったあまり 個の青いベリー
である。, なので、。よって、これ以上カゴを増やせるとしたら 1 個だけなのだ。
逆に、もし , を満たすような が存在して、ベリーの合計量が 以上であるようなカゴたちから
(赤いベリー 個、青いベリー 個)
という組を抽出していって、 の総和を で割ったあまりが になるようにできるならば、カゴを 1 個増やした解を構成できる!
DP へ
このままだと とかなので大変だ。だがよく考えると、
- dp[ i ][ j ] := 最初の i 個のビュッフェからいくつか選んで、その中から適合する (赤いベリー, 青いベリー) を選んだときの、それまでに選んだ赤いベリーの個数の総和を割ったあまりが j にできるかどうか
という風にすればよいので、状態空間の大きさは で、計算量も とかにできる。
#include <bits/stdc++.h> using namespace std; long long N, K; vector<long long> a, b; long long solve() { long long sa = 0, sb = 0; for (int i = 0; i < N; ++i) sa += a[i], sb += b[i]; long long res = sa/K + sb/K; vector<vector<bool> > dp(N+1, vector<bool>(K+1, false)); dp[0][0] = true; for (int i = 0; i < N; ++i) { for (int j = 0; j < K; ++j) { if (!dp[i][j]) continue; dp[i+1][j] = true; for (int aj = 0; aj <= min(K-1, a[i]); ++aj) { if (K-aj <= b[i]) dp[i+1][(j+aj) % K] = true; } } } bool ok = false; for (long long x = 0; x <= K; ++x) { if (x <= sa%K && K-x <= sb%K && dp[N][x]) ok = true; } if (ok) ++res; return res; } int main() { ios::sync_with_stdio(false); cin.tie(0); while (cin >> N >> K) { a.resize(N); b.resize(N); for (int i = 0; i < N; ++i) cin >> a[i] >> b[i]; cout << solve() << endl; } }