动态规划入门之硬币找零问题

看了雷霄骅的故事,为之深表惋惜,一位为科研埋头奉献发光发热的人,这件事也促使我开通博客,来记录自己一点学习的过程。

最近在看动态规划的内容,看了硬币找零问题,是一个很好的对动态规划算法入门的问题,问题描述如下:有n中硬币,面值分别为v1,v2,v3,…,vn,每种都有无限多个,给定非零整数s,可以选用多个硬币,使得面值之和恰好为s。输出所需硬币的最小值和最大值。 

动态规划关键是找出状态转移方程, 然后依据状态转移方程用递归或递推的方法来实现问题的求解。该问题中硬币数目是不限的,状态方程应重点考虑被找零的数目和硬币面值之间的关系,问题中需要得出所需硬币的最小值和最大值,我们用数组d来表示当前给定的s所需的最大数目和最少数目,状态方程即为:

d[s]=max{d[s-v[i]]+1, d[s]}      (1)

d[s]=min{d[s-v[i]]+1, d[s]}       (2)

d[s]表示总数为s时所需的硬币数,d[ s – v[i] ]  表示用总和为s-v[i]时,所需的硬币数,d[s-v[i]] +1个硬币也可使得面值恰好为s, 那么,d[s]=max{d[s-v[i]]+1, d[s]}表示递推(选择)当前方案所需的硬币数(d[s])与方案(d[s-v[i]]+1)的较大者。

下面给出分别用递归和递推的方法实现的求解:

1. 递归法

求解最大值

void df_max(int s, int v[], int n, int d[])
{

	if(s == 0)
		return ;
	if(s > 0)
	{
		for(int i =0; i<n; i++)
		{
			if(s >= v[i])
			{
				if(d[s -v[i]] == 0)
					df_max(s-v[i], v, n, d);
				d[s] = d[s] < (d[s - v[i]] + 1) ? (d[s - v[i]] + 1) : d[s];
			}
		}
	}
}
int _tmain(int argc, _TCHAR* argv[])
{
    int s=10;
    int v[] = {1,2,5};
    int n = sizeof(v)/sizeof(int);
    int *d = new int[s+1];
    memset(d, 0, sizeof(int)*(s+1));
    d[0] = 0;
    df_max(s, v, n, d);
    cout<<"The max result: "<<d[s]<<endl<<endl;
    
    for(int i=0; i<=s; i++)
    {
        cout<<d[i]<<' ';
    }
    return 0;
}

递归法求解最小值

void df(int s, int v[], int n, int d[])
{

	if(s == 0)
		return ;
	if(s > 0)
	{
		for(int i =0; i<n; i++)
		{
			if(s >= v[i])
			{
				if(d[s -v[i]] != 0)
					df(s-v[i], v, n, d);
				d[s] = d[s] > (d[s - v[i]] + 1) ? (d[s - v[i]] + 1) : d[s];
			}
		}
	}
}

int _tmain(int argc, _TCHAR* argv[])
{
	int s=10;
	int v[] = {1,2,5};
	int n = sizeof(v)/sizeof(int);
	int *d = new int[s+1];
	memset(d, 1, sizeof(int)*(s+1));
	d[0] = 0;
	df(s, v, n, d);
	cout<<"The min result: "<<d[s]<<endl;
	for(int i=0; i<=s; i++)
	{
		cout<<d[i]<<' ';
	}
	return 0;
}

2. 递推法

void c_max_min(int s, int v[], int n, int max[], int min[])
{
	for(int i=1; i <= s; i++)
		for(int j = 0; j < n; j++)
		{
			if (i >= v[j])
			{
				max[i] = max[i] < (max[i-v[j]]+1) ? (max[i-v[j]]+1) : max[i];
				min[i] = min[i] > (min[i-v[j]]+1) ? (min[i-v[j]]+1) : min[i];
			}
		}
}

int _tmain(int argc, _TCHAR* argv[])
{
	int s=10;
	int v[] = {1,2,5};
	int n = sizeof(v)/sizeof(int);
	int *max = new int[s+1];
	int *min = new int[s+1];
	memset(max, 0, sizeof(int)*(s+1));
        //注意这里初始值的设定,s为1时需要一枚硬币
	memset(min, 1, sizeof(int)*(s+1));
	min[0] = 0;
	c_max_min(s, v, n, max, min);
	cout<< max[s] << ' ' << min[s] <<endl;
	return 0;
}

参看书目:《算法竞赛入门经典》 刘汝佳 著

    原文作者:动态规划
    原文地址: https://blog.csdn.net/xuedingkai/article/details/52425321
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞