Gym103371E. Goose Coins

Goose Coins

全是观察.

题意描述

鹅币王国使用 \(n\) 种鹅币作为国家货币。第 \(i\) 种鹅币的价值为 \(c_i\) 鹅元,重量为 \(w_i\)。对于所有的 \(i\ (1 \le i \le n-1)\),都满足 \(c_{i+1}\)\(c_i\) 的倍数,且 \(c_i < c_{i+1}\)

你在鹅市场购买了价值 \(p\) 鹅元的商品,希望使用恰好 \(k\) 枚鹅币来精确支付。每种类型的鹅币你都有无限多枚,因此无需担心硬币不足的问题。

请编写一个程序,找出满足条件的 \(k\) 枚硬币的最小和最大可能总重量。如果不存在这样的硬币组合,则输出 \(-1\)​。


思路

观察到如果 \(p \not\equiv 0 \pmod {c_1}\), 那么直接输出 \(-1\) 即可.

题目要求我们使用恰好 \(k\) 个鹅币组成价值 \(p\), 倘若我们先不考虑重量, 怎么使得使用的鹅币最少.

显然, 从小到大, 对于每一个鹅币 \(i\) 我们取 \(\displaystyle \lfloor \frac{p}{c_i} \rfloor\) 个, 然后 \(p := p \bmod c_i\), 这样取出来的鹅币数量一定是最少的.

将每个鹅币取得数量称为「系数」. 例如对于 \(p = 20\), 序列 \(c = [1, 2, 6]\), 那么系数序列 \(t = [0, 1, 3]\). 因为题目中给出 \(c_{i + 1}\)\(c_i\) 的倍数, 所以系数是可以下放的. 具体来说, 对于 \(1 \le j < i \le n\), 如果我们少取 \(k\)\(c_i\), 那么就可以多取 \(\displaystyle \frac{c_i}{c_j} \cdot k\)\(c_j\) 来补足.

现在考虑重量, 我们可以列出 \(\rm{DP}\) 方程

  • \(f_{i, j, k}\) 表示从后往前枚举到第 \(i\) 种鹅币, 还需要 \(j\) 个鹅币, 当前还需要 \(k\) 个第 \(i\) 种鹅币补足前面下放的最小 / 最大代价.

不选第 \(i\) 种鹅币.

\[f_{i, j, x} = f_{i + 1, j, k} \]

其中 \(\displaystyle x = \frac{c_{i + 1}}{c_i} \cdot k +t_i\), \(t\) 即为前面所述的系数序列.

选择第 \(i\) 种鹅币, 类似于背包

\[f_{i, j, k} = \min(f_{i, j, k}, f_{i, j + 1, k + 1} + w_i) \]

时间复杂度 \(\mathcal{O}(n k^2)\).

#include <iostream>
#include <cstring>

using namespace std;

#define int long long

constexpr int N = 62, M = 1001;

int n, k, p, c[N], w[N], x[N], f[N][M][M], g[N][M][M];

void init() {
	memset(f, 63, sizeof f);
	memset(g, 128, sizeof g);
	cin >> n >> k >> p;
	f[n + 1][k][0] = g[n + 1][k][0] = 0;
	for (int i = 1; i <= n; ++i) {
		cin >> c[i] >> w[i];
	}
	for (int i = n, t = p; i; t %= c[i--]) {
		x[i] = t / c[i];
	}
}

void calculate() {
	if (p % c[1]) {
		puts("-1");
		return;
	}
	for (int i = n; i; --i) {
		for (int j = 0; j <= k; ++j) {
			for (int l = 0; l <= j; ++l) {
				int t = c[i + 1] / c[i] * l + x[i];
				if (t <= j) {
					f[i][j][t] = min(f[i][j][t], f[i + 1][j][l]);
					g[i][j][t] = max(g[i][j][t], g[i + 1][j][l]);
				}
			}
		}
		for (int j = k; j; --j) {
			for (int l = j; l; --l) {
				f[i][j - 1][l - 1] = min(f[i][j - 1][l - 1], f[i][j][l] + w[i]);
				g[i][j - 1][l - 1] = max(g[i][j - 1][l - 1], g[i][j][l] + w[i]);
			}
		}
	}
	if (g[1][0][0] < 0) {
		puts("-1");
		return;
	}
	cout << f[1][0][0] << ' ' << g[1][0][0] << '\n';
}

void solve() {
	init();
	calculate();
}

signed main() {
	solve();
	return 0;
}
posted @ 2025-03-31 16:55  Steven1013  阅读(49)  评论(0)    收藏  举报