SRM 355 300pt : MixingLiquids
keyword
二分探索 誤差 中間値の定理 C++
問題概要
濃度p[i]%の液体がa[i]リットルある。濃度need%の液体を最大でどれだけつくることができるか求める問題。
解法
答えに関して二分探索する。xリットルの液体を作るとき、濃度の低い順にxリットル取ってきたものの濃度をL, 濃度の高い順にxリットル取ってきたものの濃度をHとすると、L < need < Hならば、中間値の定理より濃度need%の液体を作ることができる。
ただし、濃度LとHを求めるときに誤差がはいるので、そこは注意してやる(二分探索の中でEPSをどうしても使わなければならないときは、許容誤差よりもかなり小さめに取っておくとよい)。
ちなみに上位陣の回答は何か整数のまま上手いことやっている。
感想
マシンイプシロンがどの位か知っておけばEPSを自信持って定めることができるんだけど、そういう苦労は避ける方向でやりたい。
#include <vector> #include <algorithm> using namespace std; struct MixingLiquids { double howMuch(vector <int> percent, vector <int> amount, int need) { vector< pair<double, double> > as; double nd = need/100.0; int N = percent.size(); double total_amount = 0.0; for(int i=0; i<N; i++){ as.push_back( make_pair(percent[i]/100.0, amount[i]) ); total_amount += amount[i]; } sort(as.begin(), as.end()); double low = 1e-10, high = total_amount; for(int loop = 0; loop < 100; loop++){ double mid = (low + high)*0.5; { double cur_amount =0.0, cur_dense = 0.0; for(int i=0; i<N; i++){ if(cur_amount + as[i].second < mid){ cur_dense = (cur_dense*cur_amount + as[i].first*as[i].second)/(cur_amount + as[i].second); cur_amount += as[i].second; } else{ cur_dense = (cur_dense*cur_amount + as[i].first*(mid - cur_amount))/mid; break; } } if(cur_dense > nd + 1e-12){ high = mid; continue; } } { double cur_amount =0.0, cur_dense = 0.0; for(int i=N-1; i>=0; i--){ if(cur_amount + as[i].second < mid){ cur_dense = (cur_dense*cur_amount + as[i].first*as[i].second)/(cur_amount + as[i].second); cur_amount += as[i].second; } else{ cur_dense = (cur_dense*cur_amount + as[i].first*(mid - cur_amount))/mid; break; } } if(cur_dense < nd - 1e-12){ high = mid; continue; } } low = mid; } return low; } };