解题思路:可以将需要凑的值看作点,初始为S,目标为0.如果现状态为i,使用一个硬币j,则状态会转移到i-vj。因为需要同时求最大最小值,用递推的方式最为简便。具体参见代码注解(推荐阅读刘汝佳书上的讲解)。
题目描述:有n种硬币,面值分别为V1,V2,…Vn,每种都有无限多。给定非负整数S,可以选用多少个硬币,使得面值之和恰好为S?输出硬币数目的最小值和最大值!
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define inf 1<<30
int minv[10000],maxv[10000],v[100];
int n,s;
int print(int s1) //这里写出的是打印最大路径的程序,最小路径类似
{
for(int i=1;i<=n;i++)
{
if(maxv[s1-v[i]]==maxv[s1]-1)
{
print(s1-v[i]);
cout<<i<<' ';
break;
}
}
}
int main()
{
while(cin>>n>>s)
{
for(int i=1;i<=n;i++)
{
cin>>v[i];
}
for(int i=0;i<=s;i++)
{
minv[i]=inf; //最小值初始定义为无穷
maxv[i]=-inf; //最大值初始定义为负无穷
}
minv[0]=0;maxv[0]=0;
for(int i=0;i<=s;i++) //使用递推求解注意顺序
{
for(int j=1;j<=n;j++)
{
if(i>=v[j]) //判断条件得加,防止数组越界
{
minv[i]=min(minv[i],minv[i-v[j]]+1);
maxv[i]=max(maxv[i],maxv[i-v[j]]+1);
}
}
}
cout<<minv[s]<<' '<<maxv[s]<<endl;
print(s);
cout<<endl;
}
return 0;
}