C - Sake or Water 解説
by
mechanicalpenciI
以下では、それぞれのカップについて、入っている無色透明な液体の量を、便宜上「カップの 容量 」と呼ぶことにします。
また、カップの容量を降順に \(A'_1,A'_2,\ldots, A'_N\) \((A'_1\geq A'_2\geq \cdots\geq A'_N)\) とします。これは、与えられる容量 \((A_1,A_2,\ldots,A_N)\) を降順にソートすることで得られます。
まず、高橋君が特定のカップの集合を選んだときに、確実に飲める日本酒の量について考えます。
まず、選んだカップの数が \((N-K)\) 個以下、すなわち水の入ったカップの数以下であった時、すべてのカップに水が入っているケースがあり得るため、確実に飲める日本酒の量は \(0\) ml となります。
そうでないとき、高橋君が飲める日本酒の量が最も少なくなるのは、「選んだカップのうち容量が大きい方から \((N-K)\) 個のカップに水が入っており、残りのすべてのカップに日本酒が入っていた」場合です。このとき、高橋君が飲める日本酒の量は選んだカップの集合から容量が大きい順に \((N-K)\) 個を除いた後に残ったカップの容量の合計となります。
なお、容量が大きい順に並べる際、同じ容量を持つカップが複数存在する場合はそれらをどの順に並べても日本酒の量は変わらないことに注意してください。
次に、高橋君の選ぶカップの個数が \(M\) \((1\leq M\leq N)\) のときの、(\(M\) 個のカップの選び方全体にわたる)「確実に飲める日本酒の量」の最大値について考えます。
\(M\leq N-K\) のときはどのように選んでも \(0\) ml になるため、最大値も \(0\) ml となります。
\(M>N-K\) の場合について考えます。まず候補となる選び方における確実に飲める日本酒の量は、選ばれたカップの中で日本酒が入っていた \(M-(N-K)\) 個のカップの容量の総和となります。また、\(N\) 個のカップ全体で大きい順に \((N-K)\) 個のカップは、上記で考えた 最も少なくなる場合において、この「選ばれたカップの中で日本酒が入っているカップ」になることはありません。なぜなら、そのカップが選ばれていないならばその時点で条件をみたしておらず、選ばれたとしても選ばれたカップの中で必ず大きい方から \((N-K)\) 個以内に入っているため、上記の場合では水が入っているからです。よって、この「確実に飲める日本酒の量」は高々、容量が大きい方から \((N-K+1)\), \((N-K+2)\), \(\ldots\), \(M\) 番目のカップの容量の総和、すなわち \((A’_{N-K+1}+A’_{N-K+2}+\cdots+A’_{M})\) ml となります。そしてこの値は達成可能です。高橋君は、容量が大きい方から \(1\), \(2\), \(\ldots\), \(M\) 番目カップを選ぶことで、確実に\((A’_{N-K+1}+A’_{N-K+2}+\cdots+A’_{M})\) ml の日本酒を飲むことができます。
ゆえに、高橋君の選ぶカップの個数が \(M\) \((1\leq M\leq N)\) のときの、確実に飲める日本酒の量の最大値は、\(M\leq N-K\) ならば \(0\) ml、\(M>N-K\) ならば、\((A’_{N-K+1}+A’_{N-K+2}+\cdots+A’_{M})\) ml となります。
したがって、これが初めて \(X\) 以上となるような最小の \(M\) を(存在しなければ \(-1\) を)出力すれば良いです。
ここで、各 \(i=1,2,\ldots, N\) について \((A’_{N-K+1}+A’_{N-K+2}+\cdots+A’_{i})\) を独立に計算しようとすると、全体で最悪 \(O(N^2)\) の計算量がかかり、実行時間制限に間に合いません。しかし、\(A’_{N-K+1}+A’_{N-K+2}+\cdots+A’_{i}=(A’_{N-K+1}+A’_{N-K+2}+\cdots+A’_{i-1})+A'_i\) に注意すると、 \(i-1\) のときの総和に \(A'_i\) を足すことで \(i\) のときの総和を得ることができるため、それぞれの \(i\) について順に \(O(1)\) で総和を求めることができ、全体で \(O(N)\) となります。最初のソートにかかる計算量とあわせて問題を解くのにかかる時間計算量は \(O(N\log N)\) であり、十分高速です。
よって、この問題を解くことができました。
C++ による実装例:
#include <bits/stdc++.h>
using namespace std;
int main(void){
int n, k;
long long x, y = 0;
cin >> n >> k >> x;
vector<long long>a(n);
for(int i=0;i<n;i++)cin>>a[i];
sort(a.begin(),a.end(),greater<long long>());
int ans=0;
for(int i=0;i<n;i++){
ans++;
if(i<(n-k))continue;
y+=a[i];
if(y>=x)break;
}
if(y<x)cout << -1 << endl;
else cout << ans << endl;
return 0;
}
投稿日時:
最終更新:
