• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
jacklee404
Never Stop!
博客园    首页    新随笔    联系   管理    订阅  订阅
货币系统-计数类背包问题

货币系统-计数类背包问题

问题

532. 货币系统 - AcWing题库

思路

题意:

​ 对于给定长度为\(n\)的序列\(a\), 对于序列\(a\)种任意数的线性组合有

​ \(s = k_1a_1 + k_2a_2 + ..., \space k_i \in Z^+\)

​ 求另一个序列满足\(a\)中能用线性组合表达的数都能在\(b\)中表示,求满足该条件的\(b\)序列的最小长度

性质:

  • 对于任意的\(a_i\)一定都能够被序列\(b\) 序列本身表达
  • 对于任意的\(b_i\)都不能被该序列的其他元素线性组合表达 (最优性)

有上述性质,可以推导对于任意的\(b_i \in a\)

证明:

​ 假设\(b_i \notin a\), 则\(b_i = xa_y + ... + ya_z = lb_k + ... + mb_c\) (第二个等式是因为对于任意的\(a_i\)都能被\(b\)序列表示)成立,并且保证\(i\)与两个等式中的下标均不相等, 即该数可以被其他数线性表出,这与最优性不符,所以\(b_i \notin a\) 不成立

解题思路:

​ 由上述性质,该问题转变为筛取序列\(a\)中能被线性表达的数后的,序列大小。

​ 可以考虑完全背包计数,因为每个数可以选定无限次,而对于任意的\(a_i\), 只能被\(k \in a, k \le a_i\) 线性表达,我们将序列\(a\)排序,通过完全背包计数可以解决该问题。

\(f[i][j]\) 表示考虑前\(i\)个数,当背包容量为\(j\)时的方案数,其中\(0 \le j \le max(a)\)

Code

#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>
using i64 = long long;

const int N = 2e4 + 5e3 + 10;

int dp[N];

void solve() {
    memset(dp, 0, sizeof dp);
	int n, m = -1, ans = 0;

	std::cin >> n;
	std::vector<int> a(n + 1);

	for(int i = 1; i <= n; i ++) {
		std::cin >> a[i];
		m = std::max(m, a[i]);
	}

	std::sort(a.begin(), a.end());

	dp[0] = 1;

	for(int i = 1; i <= n; i ++) {
	    
		if(!dp[a[i]]) ans ++;

		for(int j = a[i]; j <= m; j ++) {
			dp[j] = dp[j] + dp[j - a[i]];
		}
	}	

	std::cout << ans << "\n";
}

int main() {
	int _;
	std::cin >> _;
	while(_ --) {
		solve();
	}
}
posted on 2023-02-05 16:55  Jack404  阅读(14)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3