本题解加上了 隔壁评测链接 的额外要求,输出任意一种具体解(每一种货币用了多少个)。
类似 AcWing 12. 01背包求方案数 的做法,不做滚动数组优化,而是记录下每一种货币使用过之后是的所有 DP 值。由于本题强制要求恰好凑够,对背包容量的可达性有要求,所以把除了 $dp_{0,0}$ 之外的所有 DP 值都设成 $+\infty$ ,每一种货币当做个数是货币数、体积是面值、单个货币价值是 1 跑最小价值即可。
然后在确认了决策点之后,由于单调队列在跑的过程中,使用的物品个数有偏差,在本人的写法当中,还要利用队列中记录的使用偏移数相减,即可得到第 $i$ 种物品在这种决策下实际用了多少。
最后输出就和上面的题完全一样了,从 $n$ 开始往下,减去决策所用的货币数乘以面值即可。
const int N = 210;
const int V = 20010;
int n, m;
int cost[N], cnt[N];
int dp[N][V], pre[N][V]; // pre : counter of coin(i)
int ans_cnt[N];
int deq[V], deqv[V], s, t;
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
scanf("%d", &cost[i]);
for (int i = 1; i <= n; ++i)
scanf("%d", &cnt[i]);
scanf("%d", &m);
memset(dp, 0x3f, sizeof(dp)), dp[0][0] = 0;
for (int i = 1; i <= n; ++i)
{
for (int a = 0; a < cost[i]; ++a)
{
s = t = 0; // dq.clear()
for (int j = 0; j * cost[i] + a <= m; ++j)
{
int val = dp[i - 1][j * cost[i] + a] - j;
while (s < t && deqv[t - 1] >= val) --t;
deq[t] = j, deqv[t++] = val;
dp[i][j * cost[i] + a] = deqv[s] + j, pre[i][j * cost[i] + a] = j - deq[s];
if (deq[s] == j - cnt[i]) ++s;
}
}
}
printf("%d\n", dp[n][m]);
int pack = m, cur_cost = 0;
for (int i = n; i; --i)
ans_cnt[i] = pre[i][pack], cur_cost = pre[i][pack] * cost[i], pack -= cur_cost;
for (int i = 1; i <= n; ++i)
printf("%d ", ans_cnt[i]);
}