题解 洛谷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}
\]

浙公网安备 33010602011771号