1 条题解
-
0
感觉这题就是一个简单的背包。题面已经够简洁的了,可以直接去看题面。
思路
给你 个硬币,每个硬币的价值为 ,要凑出的数为 ,不难发现这种价值的硬币你最多使用 个。这题最坏情况下第 种硬币选的上限为 ,且 到 为 1 到 的任意排列。那么经过处理后最多有
$$\lfloor \dfrac{k}{a_1} \rfloor+\dots\lfloor \dfrac{k}{a_x} \rfloor+\dots\lfloor \dfrac{k}{a_n} \rfloor=\lfloor \dfrac{k}{1} \rfloor+\dots\lfloor \dfrac{k}{x} \rfloor+\dots\lfloor \dfrac{k}{n} \rfloor\approx k\lg(k)$$个硬币可以选择,再加上枚举当前选到了第几个硬币的复杂度,总复杂度就是 ,
吸个氧就能通过本题。细节
本题
毒瘤的要求出方案,于是我们要记录当前状态下选了几个硬币,最后再倒推每个硬币用了几次。代码
#include<bits/stdc++.h> using namespace std; const int N=2e2+10,M=2e4+10,INF=1e9; int n,a[N],b[N],k,g[N][M],f[N][M],ans[N]; //f_{i,j} 表示前 i 种硬币凑出 j 最少要用多少硬币,g_{i,j} 表示表示前 i 种硬币凑出 j 第 i 种硬币要选几个 int main(){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=n;i++) scanf("%d",&b[i]); scanf("%d",&k); for(int i=1;i<=n;i++){ b[i]=min(b[i],k/a[i]);//处理出每个硬币最多合理使用多少次 } for(int i=0;i<N;i++) for(int j=0;j<M;j++) f[i][j]=INF; for(int l=0;l<=b[1];l++){ f[1][l*a[1]]=l; g[1][l*a[1]]=l; } for(int i=2;i<=n;i++){ for(int j=0;j<=k;j++){ for(int l=0;l<=b[i];l++){ if(l*a[i]<=j){ if(f[i-1][j-l*a[i]]+l<=f[i][j]){ f[i][j]=f[i-1][j-l*a[i]]+l; g[i][j]=l; } } } } } printf("%d\n",f[n][k]); int l=k; for(int i=n;i>=1;i--){ ans[i]=g[i][l]; l-=g[i][l]*a[i]; } for(int i=1;i<=n;i++) printf("%d ",ans[i]); }感觉这么做不如楼上几位大佬的,但也能通过本题了。
- 1
信息
- ID
- 3186
- 时间
- 1000ms
- 内存
- 64MiB
- 难度
- 9
- 标签
- 递交数
- 63
- 已通过
- 3
- 上传者