虎年快乐之算法练习题22---动态规划“最少硬币问题”

文章目录


前言

2022虎年初一,祝大家新年快乐!今天借着喜气,写一篇关于算法竞赛中非常重要的一种思想----动态规划(DP)。它主要用来解决最优解问题,而其思想核心往往是把最优解细化成许多个子问题来求解,最后在一步步的利用前面的结果递进优化得到问题的最优解。而这些子问题是前后相关的,并且非常相似,且处理方法一样。下面通过非常经典的一道例题来介绍这种算法思想。

一、题目描述

有n种硬币,面值分别为v1,v2,…,vn,数量无限。输入非负整数s,选用硬币,使其和为s。要求输出最少的硬币组合。

输入样例:

5
1 5 10 25 50
251

输出样例:

6

二、DP思路

我们可以定义一个数组,int Min[MONEY],其中Min[i]对应着在凑够金额 i 下需要的最少硬币个数是多少,有了这个数组,我们就可以用来记录每一个数值下,硬币个数的最优解。动规一开始要进行初始化操作,思路就是先尝试用面值最小的硬币去装填Min[i],这样得出来的解一定是硬币最多的情况。

例如:现在有(1,5,10)三种面值的硬币

那么首先用1元面值去装填,那么Min[i]等于i(Min[0]=0),这样一定不是最优解,那么把所有金额装完后,开始尝试用5元面值去装,那么在Min[1]~Min[4]仍然用1元是最优解,但是在Min[5]时,Min[5]=min(Min[5-5]+1,Min[5]),那么Min[5]的结果显然会变为1,刚才我所用的这样一个取最小的判断过程,就是动态规划中非常重要的状态转移方程

那么依次类推,同样是在5元面值下,Min[6]=Min[6-5]+1=2,这样最终我们就可以得到所有金额的最优解个数

三、具体代码

#include<bits/stdc++.h>
using namespace std;
int n;
int money;
int Min[1000000]; //每个金额对应最少的硬币数量
int value[10];  //定义面值数组
void solve()
{
	for(int k=0;k<=money;k++)  //初始值设为无穷大
	{
		Min[k]=INT_MAX;
	}
	Min[0]=0;
	for(int j=1;j<=n;j++)
	{
		for(int i=value[j];i<=money;i++)
		{
			Min[i]=min(Min[i],Min[i-value[j]]+1);  //比较使用value[j]这种面值的硬币后,需要的硬币数量是否减少
		}
	}
}
int main()
{
	cin>>n; //n种硬币
	for(int i=1;i<=n;i++)
	{
		cin>>value[i];
	}
	cin>>money;
	solve();
	cout<<Min[money]<<endl;
	return 0;
}
上一篇:2022 LOJ 随机做题随笔——二月


下一篇:Generic Servlet 概述 | 学习笔记