题解 洛谷P10973 Coins

绝世 DP 好题!

题目

解法

首先想到背包,然而并不能做。但是至少我们知道了用动态规划解,这题也很像动态规划。

\(f_{i}\) 表示 \(i\) 能不能凑出。考虑每个数额的硬币,我们可以用它,也可以不用,但最多只能用 \(C_{i}\) 次。

每个硬币只能用 \(C_{i}\) 这个条件怎么处理呢?考虑 \(g_{i}\) 表示要凑出 \(i\) 最少需要的硬币数量。那么有以下转移方程:

\[\begin{aligned} g_{j+a_{i}}=g_{j}+1(f_{j+a_{i}}=0) \end{aligned} \]

然后就有如下状态转移方程:

\[\begin{aligned} f_{j+a_{i}} = f_{j+a_{i}} \lor f_{j}(f_{j+a_{i}}=0)(g_{j}<c_{i}) \end{aligned} \]

对于 \(f_{i}=1\) 的,我们不需要管,之前就已凑出,如果管了,还会浪费用的硬币次数。

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int f[N],g[N];
int a[N],c[N],n,m;
signed main() {
	while(cin>>n>>m,n||m) {
		for(int i=1;i<=n;i++) cin>>a[i];
		for(int i=1;i<=n;i++) cin>>c[i];
		memset(f,0,sizeof f);f[0]=1;
		for(int i=1;i<=n;i++) {
			memset(g,0,sizeof g);
			for(int j=1;j<=m;j++) {
				if(j-a[i]>=0&&g[j-a[i]]+1<=c[i]&&f[j]==0) {
					g[j]=g[j-a[i]]+1;
					f[j]|=f[j-a[i]];
				}
			}
		}
		int cnt=0;
		for(int i=1;i<=m;i++) if(f[i]) cnt++;
		cout<<cnt<<endl;
	}
	return 0;
} 

思路

我们发现,实际上我们在限制一个物品只能使用 \(C_{i}\) 下足了功夫,这也是一个影响一个算法到底可以不可以的标准,因为我们实际上无法去掉枚举物品与枚举目前凑出的钱的时间,即,这两个是具体决定算法的。所以我们有没有什么办法能让这个 \(C_{i}\) 被去掉呢。我们知道,一个硬币的运用次数是和凑出的钱紧密结合的,因为在转移到另一个钱时,我们要考虑当前的硬币还能不能再用,为什么,我们不能提前算出在一个特定的钱时,这个硬币最少会用多少次呢?这样我们就不需要枚举当前用的次数了!并且当他最少时,后面拓展的机会一定最大,所以绝对可以。

关于顺序,其实我们可以随意交换考虑物品的顺序,因为我们只在乎选的那些总和,但是对于凑出的钱,必须对于单一物品满足单调性。也就是告诉我们,我们只要对于每个物品凑出的钱单调转移即可,以下转移也是对的:

\[\begin{aligned} f_{1}=f_{1} \lor f_{1-a_{1}}\\ f_{1}=f_{1} \lor f_{1-a_{2]}\\ f_{2}=f_{2} \lor f_{2-a_{2}}\\ f_{3}=f_{3} \lor f_{3-a_{2}}\\ f_{2}=f_{2} \lor f_{2-a_{1}}\\ \end{aligned} \]

posted @ 2024-08-26 21:16  PM_pro  阅读(31)  评论(0)    收藏  举报