动态规划:买书问题

有一书店引进了一套书,共有3卷,每卷书定价是100元,书店为了搞促销,推出一个活动,活动如下:
       如果单独购买其中一卷,那么可以打9.5折。
       如果同时购买两卷不同的,那么可以打9折。
       如果同时购买三卷不同的,那么可以打8.5折。

       如果小明希望购买第1卷x本,第2卷y本,第3卷z本,那么至少需要多少钱呢?(x、y、z为三个已知整数)。

首先三卷书的价格一样,因此三卷书可以无差别对待,比如买3本第一卷,2本第二卷和1本第三卷,和买1本第一卷,2本第二卷,三本第三卷属于同一种情况。

1.一次买书过程可以买三本、两本或一本。而每经过一次选择,剩下的问题又变成给定一定数量的书,求花钱最少的方案。即原问题可以分解为若干子问题。

2.状态转移方程

设F(x,y,z)为当购买x本一卷,y本二卷,z本三卷时的最小花费。假定x>=y>=z

F(x,y,z)=0(x=y=z=0)

F(x,y,z)=min{

F(x-1,y-1,z-1)+3*100*0.85,    (z>=1,购买三卷各一本)

F(x-1,y-1,z)+2*100*0.9,             (y>=1,购买两卷)

F(x-1,y,z)+100*0.95  (x>=1,,购买一卷)

}

实现代码如下:

public class BuyBook {
	static int Max=100000;
	static int count=0;
	static int MinMoney[][][]=new int[100][100][100];
	public static void main(String [] args){
		int x = 2,y = 5,z = 8;
		
		System.out.println(minMoney(x,y,z));
		System.out.println("调用minMoney函数的次数"+count);
	}

	private static int minMoney(int x, int y, int z) {
		// TODO Auto-generated method stub
		count++;
		int [] num={x,y,z};
		Arrays.sort(num);
		x=num[2];
		y=num[1];
		z=num[0];
		System.out.println("x:"+x+"  y:"+y+"  z:"+z);
		if(MinMoney[x][y][z]!=0)return MinMoney[x][y][z];
		if(x==0)return 0;
		else if(z>=1){
			MinMoney[x][y][z]= min(minMoney(x-1,y-1,z-1)+255,minMoney(x-1,y-1,z)+180,minMoney(x-1,y,z)+95);
		}else if(y>=1){
			MinMoney[x][y][z]= min(Max,minMoney(x-1,y-1,z)+180,minMoney(x-1,y,z)+95);
		}else if(x>=1){
			MinMoney[x][y][z]= min(Max,Max,minMoney(x-1,y,z)+95);
		}
		return MinMoney[x][y][z];
	}


	private static int min(int i, int j, int k) {
		// TODO Auto-generated method stub
		if(i<=j&&i<=k)return i;
		else return j<k?j:k;
	}
}

以下总结参考自博客 点击打开链接

接下来,我们就进行一下总结:

    递归到动规的一般转化方法

    递归函数有n个参数,就定义一个n维的数组,数组的下标是递归函数参数的取值范围,数组元素的值是递归函数的返回值,这样就可以从边界值开始, 逐步填充数组,相当于计算递归函数值的逆过程。

    动规解题的一般思路

    1. 将原问题分解为子问题

  •     把原问题分解为若干个子问题,子问题和原问题形式相同或类似,只不过规模变小了。子问题都解决,原问题即解决(数字三角形例)。
  •     子问题的解一旦求出就会被保存,所以每个子问题只需求 解一次。

    2.确定状态

  •     在用动态规划解题时,我们往往将和子问题相关的各个变量的一组取值,称之为一个“状 态”。一个“状态”对应于一个或多个子问题, 所谓某个“状态”下的“值”,就是这个“状 态”所对应的子问题的解。
  •     所有“状态”的集合,构成问题的“状态空间”。“状态空间”的大小,与用动态规划解决问题的时间复杂度直接相关。 在数字三角形的例子里,一共有N×(N+1)/2个数字,所以这个问题的状态空间里一共就有N×(N+1)/2个状态。

    整个问题的时间复杂度是状态数目乘以计算每个状态所需时间。在数字三角形里每个“状态”只需要经过一次,且在每个状态上作计算所花的时间都是和N无关的常数。

    3.确定一些初始状态(边界状态)的值

    4. 确定状态转移方程

     定义出什么是“状态”,以及在该“状态”下的“值”后,就要找出不同的状态之间如何迁移――即如何从一个或多个“值”已知的 “状态”,求出另一个“状态”的“值”(递推型)。状态的迁移可以用递推公式表示,此递推公式也可被称作“状态转移方程”。

    能用动规解决的问题的特点

    1) 问题具有最优子结构性质。如果问题的最优解所包含的 子问题的解也是最优的,我们就称该问题具有最优子结 构性质。

    2) 无后效性。当前的若干个状态值一旦确定,则此后过程的演变就只和这若干个状态的值有关,和之前是采取哪种手段或经过哪条路径演变到当前的这若干个状态,没有关系。



已标记关键词 清除标记
相关推荐
买书问题 dp实现 题目:买书 有一书店引进了一套书,共有3卷,每卷书定价是60元,书店为了搞促销,推出一个活动,活动如下: 如果单独购买其中一卷,那么可以打9.5折。 如果同时购买两卷不同的,那么可以打9折。 如果同时购买三卷不同的,那么可以打8.5折。 如果小明希望购买第1卷x本,第2卷y本,第3卷z本,那么至少需要多少钱呢?(x、y、z为三个已知整数)。 1、过程为一次一次的购买,每一次购买也许只买一本(这有三种方案),或者买两本(这也有三种方案), 或者三本一起买(这有一种方案),最后直到买完所有需要的书。 2、最后一步我必然会在7种购买方案中选择一种,因此我要在7种购买方案中选择一个最佳情况。 3、子问题是,我选择了某个方案后,如何使得购买剩余的书能用最少的钱?并且这个选择不会使得剩余的书为负数 。母问题和子问题都是给定三卷书的购买量,求最少需要用的钱,所以有"子问题重叠",问题中三个购买量设置为参数, 分别为i、j、k。 4、的确符合。 5、边界是一次购买就可以买完所有的书,处理方式请读者自己考虑。 6、每次选择最多有7种方案,并且不会同时实施其中多种,因此方案的选择互不影响,所以有"子问题独立"。 7、我可以用minMoney[i][j][k]来保存购买第1卷i本,第2卷j本,第3卷k本时所需的最少金钱。 8、共有x * y * z个问题,每个问题面对7种选择,时间为:O( x * y * z * 7) = O( x * y* z )。 9、用函数MinMoney(i,j,k)来表示购买第1卷i本,第2卷j本,第3卷k本时所需的最少金钱,那么有: MinMoney(i,j,k)=min(s1,s2,s3,s4,s5,s6,s7),其中s1,s2,s3,s4,s5,s6,s7分别为对应的7种方案使用的最少金钱: s1 = 60 * 0.95 + MinMoney(i-1,j,k) s2 = 60 * 0.95 + MinMoney(i,j-1,k) s3 = 60 * 0.95 + MinMoney(i,j,k-1) s4 = (60 + 60) * 0.9 + MinMoney(i-1,j-1,k) s5 = (60 + 60) * 0.9 + MinMoney(i-1,j,k-1) s6 = (60 + 60) * 0.9 + MinMoney(i-1,j,k-1) s7 = (60 + 60 + 60) * 0.85 + MinMoney(i-1,j-1,k-1)
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页