for(int i=1;i<=tot;i++)//tot:拆分后的新硬币总数
for(int j=m;j>=val[i];j--)//val[i]:拆分后的第i个新硬币的面值
f[j]=min(f[j],f[j-val[i]]+c[i])//c[i]:第i枚新硬币实际上组成的硬币数
int sum=f[m];//sum:最少需用货币数(第一问答案)
while(sum){
while(!to[tot][m])tot--;//找到是从哪个新硬币转移到当前面值m的
m-=val[tot];//m转移到上一次的面值
ans[val[tot]/c[tot]]+=c[tot];//因为一枚新硬币是由若干个面值相同的旧硬币组成,故val[tot]/c[tot]就是该旧硬币的面值
sum-=c[tot];//sum转移到上一次面值所用的货币数
tot--;
}
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
return s*w;
}
const int N=205;
const int M=20005;
int b[N],c[3005],val[3005],f[M],ans[M];
bool to[3005][M];
int main(){
int n=read(),tot=0;
for(int i=1;i<=n;i++)b[i]=read();
//二进制拆分模板
for(int i=1;i<=n;i++){
int C=read();
for(int j=1;j<=C;j=j<<1){
val[++tot]=j*b[i];
c[tot]=j;
C-=j;
}
if(C){
val[++tot]=C*b[i];
c[tot]=C;
}
}
//01背包模板:
memset(f,0x3f,sizeof(f));f[0]=0;
int m=read();
for(int i=1;i<=tot;i++)
for(int j=m;j>=val[i];j--)
if(f[j]>f[j-val[i]]+c[i]){
f[j]=f[j-val[i]]+c[i];
to[i][j]=1;
}
printf("%d\n",f[m]);
//输出方案:
int sum=f[m];
while(sum){
while(!to[tot][m])tot--;
m-=val[tot];
ans[val[tot]/c[tot]]+=c[tot];
sum-=c[tot];
tot--;
}
for(int i=1;i<=n;i++)printf("%d ",ans[b[i]]);puts("");
return 0;
}
原文:https://www.cnblogs.com/PPXppx/p/10996608.html