动态规划算法

本文介绍如何利用动态规划解决特定面值硬币组合成目标金额的问题,并给出详细的求解过程及Java实现。
//题目:如果需要筹够101元,现在有面值为 2元,3元,5元的硬币,显然不能用贪心算法,如果使用贪心算法101%5==1,那么就无法拼成101元
//所以我们使用动态规划来解决,动态规划有点类似分治法的思想和递归思想,不同的是在递归思想中每次的结果是确定的,而动态规划的本次的结果

//都是前面某次的结果和本次可用硬币中最优解的一种方式,而不一定是它的上一次。动态规划每次要求得到的是最优解。



//思路如下
/*
*  我们用函数f(s)来表示s元需要硬币f(s)枚  s=101,硬币面值有2,3,5三种,有最有解情况我们标识为 1,否则标识为 0


   f(0):0元时显然没有任何硬币满足条件,这时候算是有解的:就是 f(0)=f(0)=0                  1
   f(1):1元时显然没有任何硬币满足条件,   这时候算是无解的: 就是 f(1)=f(1)=0                0
   f(2):2元时显然有一种面值硬币满足条件, 这时候算是有解的: 就是 f(2)=f(2-2)+1=f(0)+1=1     1
   f(3):3元时显然有两种面值硬币满足条件, 这时候算是有解的: 就是 f(3)=f(3-3)+1=f(0)+1=1     1
   f(4):4元时显然有两种面值硬币满足条件, 这时候算是有解的: 就是 f(4)=f(4-2)+1=f(2)+1=2     1
   f(5):5元时显然有三种面值硬币满足条件, 这时候算是有解的: 就是 f(5)=f(5-5)+1=f(0)+1=1     1
   f(6):6元时显然有三种面值硬币满足条件, 这时候算是有解的: 就是 f(6)=f(6-3)+1=f(3)+1=2     1
       从这里开始分割,下面我们可以看到只要计算最优解到f(6),后面的最优解都是一种思想,为什么要计算到f(6)是因为避免选了本次最优解
      后,剩下的金额得不到组合,最后出现无解,如:f(6)=f(6-5)+1=f(1)+1,剩下的f(1)是无解的。所以只要我们计算到金额减去最优解的硬币最大面值后剩下的金额都是其它面值的倍数或者组合数即可。这里硬币面值是2,3,5时,只需要我计算到减去最大面值5后剩下的金额等于1就可以。其它都是2,3的组合或者他们的倍数,所以不会出现无解的情况。


   f(7):7元时显然有三种面值硬币满足条件, 这时候算是有解的: 就是 f(7)=f(7-5)+1=f(2)+1=2     1
   f(8):8元时显然有三种面值硬币满足条件, 这时候算是有解的: 就是 f(8)=f(8-5)+1=f(3)+1=2     1
   f(9):9元时显然有三种面值硬币满足条件, 这时候算是有解的: 就是 f(9)=f(9-5)+1=f(4)+1=3     1
   f(10):10元时显然有三种面值硬币满足条件,这时候算是有解的:就是 f(10)=f(10-5)+1=f(5)+1=2  1
   f(11):11元时显然有三种面值硬币满足条件,这时候算是有解的:就是 f(11)=f(11-5)+1=f(6)+1=3  1
   f(12):12元时显然有三种面值硬币满足条件,这时候算是有解的:就是 f(12)=f(12-5)+1=f(7)+1=3  1
   f(13):13元时显然有三种面值硬币满足条件,这时候算是有解的:就是 f(13)=f(13-5)+1=f(8)+1=3  1


package com.pan;

import java.util.Scanner;

public class Packgeproblem {
	
	
	public int fun(int i) {
		if(i==0) {
			System.out.println("用了一个0元的硬币");
			return 0;
		}if(i==2) {
			System.out.println("用了一个2元的硬币");
			return 1;
		}if(i==3) {
			System.out.println("用了一个3元的硬币");
			return 1;
		}if(i==4) {
			System.out.println("用了一个2元的硬币");
			System.out.println("用了一个2元的硬币");
			return 2;
		}if(i==5) {
			System.out.println("用了一个5元的硬币");
			return 1;
		}if(i==6) {
			System.out.println("用了一个3元的硬币");
			System.out.println("用了一个3元的硬币");
			return 2;
		}
		System.out.println("用了一个5元的硬币");
		return fun(i-5)+1;
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//题目:如果需要筹够101元,现在有面值为 2元,3元,5元的硬币,显然不能用贪心算法,如果使用贪心算法101%5==1,那么就无法拼成101元
		//所以我们使用动态规划来解决,动态规划有点类似分治法的思想和递归思想,不同的是在递归思想中每次的结果是确定的,而动态规划的本次的结果
		//都是前面某次的结果和本次可用硬币中最优解的一种方式,而不一定是它的上一次。动态规划每次要求得到的是最优解
		//思路如下
		/*
		 *  我们用函数f(s)来表示s元需要硬币f(s)枚  s=101,硬币面值有2,3,5三种,有最有解情况我们标识为 1,否则标识为 0

   f(0):0元时显然没有任何硬币满足条件,这时候算是有解的:就是 f(0)=f(0)=0                  1
   f(1):1元时显然没有任何硬币满足条件,   这时候算是无解的: 就是 f(1)=f(1)=0                0
   f(2):2元时显然有一种面值硬币满足条件, 这时候算是有解的: 就是 f(2)=f(2-2)+1=f(0)+1=1     1
   f(3):3元时显然有两种面值硬币满足条件, 这时候算是有解的: 就是 f(3)=f(3-3)+1=f(0)+1=1     1
   f(4):4元时显然有两种面值硬币满足条件, 这时候算是有解的: 就是 f(4)=f(4-2)+1=f(2)+1=2     1
   f(5):5元时显然有三种面值硬币满足条件, 这时候算是有解的: 就是 f(5)=f(5-5)+1=f(0)+1=1     1
   f(6):6元时显然有三种面值硬币满足条件, 这时候算是有解的: 就是 f(6)=f(6-3)+1=f(3)+1=2     1
       从这里开始分割,下面我们可以看到只要计算到硬币最大面值+剩下最多金额导致无解的的金额数,后面的最优解都是一种思想,为什么要计算到f(6)是因为避免选了本次最优解
      后,剩下的金额得不到组合,最后出现无解,如:f(6)=f(6-5)+1=f(1)+1,剩下的f(1)是无解的。所以只要我们大于6后剩下的金额都是有解的。

   f(7):7元时显然有三种面值硬币满足条件, 这时候算是有解的: 就是 f(7)=f(7-5)+1=f(2)+1=2     1
   f(8):8元时显然有三种面值硬币满足条件, 这时候算是有解的: 就是 f(8)=f(8-5)+1=f(3)+1=2     1
   f(9):9元时显然有三种面值硬币满足条件, 这时候算是有解的: 就是 f(9)=f(9-5)+1=f(4)+1=3     1
   f(10):10元时显然有三种面值硬币满足条件,这时候算是有解的:就是 f(10)=f(10-5)+1=f(5)+1=2  1
   f(11):11元时显然有三种面值硬币满足条件,这时候算是有解的:就是 f(11)=f(11-5)+1=f(6)+1=3  1
   f(12):12元时显然有三种面值硬币满足条件,这时候算是有解的:就是 f(12)=f(12-5)+1=f(7)+1=3  1
   f(13):13元时显然有三种面值硬币满足条件,这时候算是有解的:就是 f(13)=f(13-5)+1=f(8)+1=3  1
		*/
		Scanner sc=new Scanner(System.in);
	
	   System.out.println("请输入需要组合的总金额:");
	   int s=sc.nextInt();
	   if(s>6) {
		   int n=new Packgeproblem().fun(s);
		   System.out.println(n);	 
	   }
	  
			
		}
		

}


package com.pan;

import java.util.Scanner;

public class Packgeproblem {
	int a[];
	int j=0;
	int s[];
	int n=0;
	public Packgeproblem(int t,int s[]) {
		a=new int[t];
		this.s=s;
	}
	
	/*
	 * 动态规划
	*/
	public int fun(int i) {
			if(i>=1&&s[i]>s[i-1]) {
				return a[++j]=fun(--i)+1;
			}else if(i>=0){
				a[++j]=1;
				if(i>=1) {
					fun(i-1);
				}
				return 1;
			}	
			return 0;
	}
	
	public void kun() {
		for(int i=1;i<a.length;i++) {
			System.out.println(a[i]);
		}
	}

	public static void main(String[] args) {
	
	  Scanner sc=new Scanner(System.in);
	   int s[]= {2,4,6,8,3,5,10,9,11,12,13,0,1,15,17,18,19,20};
	   Packgeproblem p=new Packgeproblem(s.length+1,s);
	   if(s.length>=1) {
		   p.fun(s.length-1);
		   p.kun();
	   }
	  	
	
	}
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值