bzoj3233 [Ahoi2013]找硬币

a[i]表示第i个数是第i-1个数的多少倍 a[1]=1
那么第i个数b[i]=ij=1a[j]
那么对于一个价格为n的物品第i个数用的次数nb[i]%a[i+1]
所以我们可以考虑dp,f[i]表示最后一个数为,然后除了i以外前面的数的最小的硬币数量是多少.

#include<cstdio>
#include<algorithm>
#include<cstring>
const int N = 100005;

int f[N], n, a[51], ret;

void G (int &num) {
    static char a; static bool fl;
    for (a = getchar (), fl = false; a > '9' || a < '0'; a = getchar ()) if (a == '-') fl = true;
    for (num = 0; a >= '0' && a <= '9'; a = getchar ()) num = (num << 3) + (num << 1) + a - '0';
    if (fl) num = -num;
}

void Min (int &x, int y) { if (x > y) x = y; }

int main () {
//  freopen ("3233.in", "r", stdin);
    int S = 0;
    G (n);
    for (int i = 1; i <= n; ++i) G (a[i]);
    std :: sort (a + 1, a + n + 1); S = a[n];
    memset (f, 0x3f, sizeof f);
    f[1] = 0;
    for (int i = 1; i <= S; ++i) {
        for (int j = 2; i * j <= S; ++j) {
            ret = 0;
            int &tx = f[i * j];
            for (int k = n; k; --k) {
                ret += a[k] / i % j;
                if (f[i] + ret > tx) break; 
            } 
            Min (tx, f[i] + ret);
        }
    }
    int ans = 0x3f3f3f3f;
    for (int i = 1; i <= S; ++i) {
        ret = 0;
        for (int k = 1; k <= n; ++k) ret += a[k] / i;
        f[i] += ret;
        if (ans > f[i]) ans = f[i];
    }
    printf ("%d\n", ans);
    return 0;
}
posted @ 2017-03-08 16:57  DraZxlnDdt  阅读(98)  评论(0编辑  收藏  举报