AOJ 2305 Beautiful Currency

2016/05/02 (Mon) AOJ DP

問題

問題文

KM国には $n$ 種類の効果があり,それぞれの価値は $a[i]$ 円です. さて,それを改正して $b[i]$ にすることになりました. $b[i]$ の美しさは $\max{ |a[i]-b[i]| / a[i] }$ で決まり,小さいほど美しいとします. もっとも美しい組み合わせの美しさを求めてください.

方針

$dp[i][j] = i$ 番目の硬化を $j$ 番目にするときの美しさの最小値. $dp[i][j]$ から $dp[i][k]$ ($k$ は $i$ の倍数) に遷移する. $k$ はあんまり大きくしても無駄なので適当な値 $K$ で打ち切る. すると,計算量は $n \times (K/1 + K/2 + \cdots + K/K)$ になる. この形の分数の列は調和数列と言って $K \log K$ で抑えられるので計算量は $O(n K \log K)$. $K$ はいくつまで必要なのか分からないが 150000 で間に合った.

実装

#include <cstdlib>
#include <cmath>
#include <climits>
#include <cfloat>
#include <cassert>
#include <map>
#include <utility>
#include <set>
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include <algorithm>
#include <functional>
#include <sstream>
#include <deque>
#include <complex>
#include <stack>
#include <queue>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <ctime>
#include <iterator>
#include <bitset>
#include <numeric>
#include <list>
#include <iomanip>
#include <cassert>
#include <array>
#include <tuple>
#include <initializer_list>
#include <unordered_set>
#include <unordered_map>
#include <forward_list>

using namespace std;
#define all(c) begin(c), end(c)
#define rep(i,n) for(int i = 0; i < (int)(n); ++i)
using ll = long long;
using pii = pair<int, int>;
#define VV(T) vector<vector< T > >

int n;
int a[25];
double dp[25][150000];
double inf = 1e9;

int main() {
    while (cin >> n) {
        for (int i = 0; i < n; ++i) {
            cin >> a[i];
        }
        fill((double*)begin(dp), (double*)end(dp), inf);
        dp[0][1] = 0;
        for (int i = 0; i < n; ++i) {
            for (int j = 1; j < 150000; ++j) {
                for (int k = j; k < 150000; k += j) {
                    double nxt = dp[i][j];
                    nxt = max(nxt, abs((double)(a[i] - k) / a[i]));
                    dp[i + 1][k] = min(dp[i + 1][k], nxt);
                }
            }
        }
        printf("%.10lf\n", *min_element(begin(dp[n]), end(dp[n])));
    }
}