DP经典应用(五)硬币问题

来源:互联网 发布:科怀莱昂纳德体测数据 编辑:程序博客网 时间:2024/06/06 09:47

问题描述:

题目:有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;

0 0
原创粉丝点击