问题描述:
题目:有n种硬币,面值分别为V1,V2,…Vn,每种都有无限多。给定非负整数S,可以选用多少个硬币,使得面值之和恰好为S?输出硬币数目的最小值和最大值
分析:
按照解决动态规划的前3个步骤我们进行分析:
1.刻画一个最优解的结构特征:
定义minv[i]和maxv[i]分别为面值为i时的最少硬币数目和最多硬币数目。
而minv[0] = max[0] = 0;这就是边界值,因为面值为0时,什么面值的硬币都不能选。2.递归地定义最优解的值:
把每个面值看做一个点,表示还需凑足的面值,则初始状态为S,目标状态为0。
有两个状态
1.不使用一个硬币,那么还是minv[i] , maxv[i]
2.使用一个硬币,则状态转移到 i - Vj 。两者之一。
那么状态转移方程为:
minv[i] = min( minv[i] , minv[i-V[j]]+1);
maxv[i] = max(maxv[i] , maxv[i-V[j]+1);3.计算最优解的值,采用自底向上的递推法。
- 4.利用计算出的信息构造出一个最优解(就是下面代码中的打印解的部分)
代码如下:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 100+5;
const int INF = 100000000;
int V[maxn],maxv[maxn],minv[maxn];
int n,s;
/*打印解*/
void print_ans(int* d,int s)
{
for(int i=1; i<=n; i++)
{
if(s>=V[i] && d[s]==d[s-V[i]]+1)
{
printf("%d ",i);
print_ans(d,s-V[i]);
break;
}
}
}
int main()
{
scanf("%d%d",&n,&s);
for(int i=1; i<=n; i++)
{
scanf("%d",&V[i]);
}
minv[0] = maxv[0] = 0;
for(int i=1; i<=s; i++)
{
minv[i] = INF;
maxv[i] = -INF;
}
for(int i=1; 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);
}
}
}
printf("%d %d\n",minv[s],maxv[s]);
print_ans(minv,s);
printf("\n");
print_ans(maxv,s);
return 0;
}
注意打印最优解时要及时break;