POJ 1742 Coins 多重背包(单调队列优化)

来源:互联网 发布:各国网络制式 编辑:程序博客网 时间:2024/05/22 10:42

题意:给定n,m,表示有n种硬币,接下来给定两个数组A[i],C[i]分别表示第i种硬币的面值以及个数,问用这些硬币可以凑出多少[1,m]范围内的数额。输入以n,m都为0结束。

首先可以转换为背包模型,f[i][j]表示前i种硬币是否可以凑出数额j,利用滚动数组可以优化第一维。

对于一个多重背包的模型,可以很容易地写出来这道题的转移方程:f[j] = max(f[j-k*A[i]]),0<=k<=C[i]
写完整是f[i][j] = max(f[i-1][j-k*A[i]]),可以看出来,对于一个状态f[i][j],它的决策是固定的一些状态f[i-1][j-k*A[i]],那么我们可以通过单调队列优化,把这些状态放到单调队列中,来做到O(1)决策。
为了做到这个,会稍微改变一下f[i]这一维中的m个状态的求解顺序:正常情况下,写的循环是

for (int i = 1; i <= n; i++)    for (int k = 0; k <= C[i]; k++)        for (int j = m; j-k*A[i] >= 0; j--)            f[i][j] = max (f[i][j], f[i-1][j-k*A[i]);

f[i]是按顺序求出来的。
而使用单调队列优化后,就变成了模A[i]意义下的每个同余类的顺序球出来。

for (int i = 1; i <= n; i++)for (int mod = 0; mod < A[i]; mod++){    q = h = 0;    for (int j = mod; j <= m; j+=A[i])    {        /*……*/    }}

这道题的单调队列很好写(方程那么简单都明摆着了)。
可以加个优化,当C[i] == 1时用普通背包策略,A[i]*C[i] >= m时用完全背包的策略。

#include <cstdio>#include <cstring>#include <algorithm>#define MX 100005using namespace std;int n, m, h, t, A[1005], C[1005];bool f[MX], q[MX];int main (){    while (~scanf ("%d %d", &n, &m) && n+m)    {        for (int i = 1; i <= n; i++) scanf ("%d", A+i);        for (int i = 1; i <= n; i++) scanf ("%d", C+i);        memset (f, 0, sizeof f);        int ans = 0; f[0] = 1;        for (int i = 1; i <= n; i++)        {            if (C[i] == 1)            {                for (int j = m; j >= A[i]; j--)                if (!f[j] && f[j-A[i]]) f[j] = 1, ans++;            }            else if (A[i] * C[i] > m)            {                for (int j = A[i]; j <= m; j++)                if (!f[j] && f[j-A[i]]) f[j] = 1, ans++;            }            else            {                for (int mod = 0; mod < A[i]; mod++)                {                       int sum = 0; h = t = q[0] = 0;                    for (int j = mod; j <= m; j+=A[i])                    {                        if (t-h == C[i]+1) sum -= q[++h];                        sum += (q[++t] = f[j]);                        if (!f[j] && sum) f[j] = 1, ans++;                    }                }            }        }        printf ("%d\n", ans);    }    return 0;}
0 0
原创粉丝点击