結果
問題 | No.31 悪のミックスジュース |
ユーザー | codershifth |
提出日時 | 2015-07-31 07:20:31 |
言語 | C++11 (gcc 11.4.0) |
結果 |
AC
|
実行時間 | 4 ms / 5,000 ms |
コード長 | 4,017 bytes |
コンパイル時間 | 1,571 ms |
コンパイル使用メモリ | 161,380 KB |
実行使用メモリ | 5,376 KB |
最終ジャッジ日時 | 2024-09-14 15:52:02 |
合計ジャッジ時間 | 2,197 ms |
ジャッジサーバーID (参考情報) |
judge1 / judge6 |
(要ログイン)
テストケース
テストケース表示入力 | 結果 | 実行時間 実行使用メモリ |
---|---|---|
testcase_00 | AC | 2 ms
5,248 KB |
testcase_01 | AC | 3 ms
5,376 KB |
testcase_02 | AC | 2 ms
5,376 KB |
testcase_03 | AC | 3 ms
5,376 KB |
testcase_04 | AC | 3 ms
5,376 KB |
testcase_05 | AC | 4 ms
5,376 KB |
testcase_06 | AC | 1 ms
5,376 KB |
testcase_07 | AC | 2 ms
5,376 KB |
testcase_08 | AC | 2 ms
5,376 KB |
testcase_09 | AC | 3 ms
5,376 KB |
testcase_10 | AC | 2 ms
5,376 KB |
testcase_11 | AC | 1 ms
5,376 KB |
testcase_12 | AC | 3 ms
5,376 KB |
testcase_13 | AC | 2 ms
5,376 KB |
testcase_14 | AC | 2 ms
5,376 KB |
testcase_15 | AC | 1 ms
5,376 KB |
testcase_16 | AC | 2 ms
5,376 KB |
ソースコード
#include <bits/stdc++.h> typedef long long ll; typedef unsigned long long ull; #define FOR(i,a,b) for(int (i)=(a);i<(b);i++) #define REP(i,n) FOR(i,0,n) #define RANGE(vec) (vec).begin(),(vec).end() using namespace std; class EvilMixJuice { public: void solve(void) { int N; ll V; cin>>N>>V; vector<ll> C(N); REP(i,N) cin>>C[i]; if (V <= N) { // 全てのジュースを 1 リットルずつ買って等分をまぜればよいだけ cout<<accumulate(RANGE(C),0LL)<<endl; return; } // // のこり V リットル分を // // p[0]>=p[1]>=...>=p[N-1]>=0 // p[0]+...+p[N-1] = V // // なるように最小コストで買えばよい。 // // 通常の dp でやると O(N*V) となってしまい TLE する。 // // そこで S[i] = C[0]+...+C[i] として // (i+1) リットルをコスト S[i] で購入すると考える。 // // 単価 S[i]/(i+1) が最小の (i+1,S[i]) を目一杯買えばよさそう。 // ただし (i+1) の倍数リットルの購入だと過不足が生じてしまう。 // // S[i]/(i+1) が最小の i を minI , m = (minI+1) とするとき // (m,S[m-1]) パック以外のパックをどれだけ買えばよいかを DP で計算する。 // // m*k リットル買うとすると (k,S[k-1]) を m 個買うよりは安い // ので (m,S[m-1]) 以外の (k,S[k-1]) の購入総数は最大 m までと考えてよい // // このとき minI!=k のすべての (k,S[k-1]) の購入総量は最大 m*N となる。 // // (1,S[0]) が最小の 1 リットルパックなので各 (k,S[k-1]) は最大 m*N 個買うことになる。 // // よって時間計算量 O(m^2*N^2) <= O(N^4) // // 購入総数が m を超えてしまうなら (m,S[m-1]) パック 1 個におきかえればよいので // (m,S[m-1]) 以外のパックの購入総数は最大 m 未満である。 // // よって O(N^3) で計算できる。 // vector<ll> S(N,0); S[0] = C[0]; FOR(i,1,N) S[i] = S[i-1]+C[i]; // 必ず 1 リットル以上は買うので先に計算しておく V = max(0LL, V-N); // S[i]/(i+1) が最大となる minI を求める ll minI = -1; ll minS = (1<<30); REP(i,N) { if (minS*(i+1) >= S[i]*(minI+1)) // minS/(minI+1) >= S[i]/(1+i) { minI = i; minS = S[i]; } } ll m = minI+1; ll mN2 = max(m*N+1, V-(V-m*N)/m*m+1); // dp[i][j] := i 番目までの S を使って j リットル買うときの最小コスト vector<ll> dp(mN2+1,0); dp[0] = 0; for (ll i = 0; i < mN2; ++i) dp[i+1] = dp[i] + S[0]; // dp[i][j+1] = dp[i][j] + S[0] // O(N^3) for (ll i = 0; i < N; ++i) for (ll j = 0; j <= mN2; ++j) { ll k = min(mN2, j+(i+1)); dp[k] = min(dp[k], dp[j]+S[i]); } // V のうち m*(N+1) までは dp で計算した結果を使う。 // 残りを (m,S[m-1]) パックで買えるだけ買う ll kk = max(0LL,(V-m*N)/m); cout<<kk*S[m-1]+dp[V-kk*m]+S[N-1]<<endl; } }; #if 1 int main(int argc, char *argv[]) { ios::sync_with_stdio(false); auto obj = new EvilMixJuice(); obj->solve(); delete obj; return 0; } #endif