动态规划特训:硬币问题(转化为DAG求最长最短路径)

解题思路:可以将需要凑的值看作点,初始为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;
}

猜你喜欢

转载自blog.csdn.net/mavises/article/details/81987071