我的java面试编程题——记录每一天(19.4.14.1)

//日期:19.4.14
//作者:***
//功能:我的面试编程题目练习十三
//============================================================
//问题描述:
//贪心算法求解背包问题,有一个容量为M的背包,现在有n中物品的重量分别为W1,w2,w3...
//而每种物品对应的价值是p1,p2,p3...现要求往背包中放入物品或物品的一部分x1,x2...
//求在不超过背包容量的要求下,该背包里放入物品的最大价值收益是多少?
//============================================================
//思路方法:
//首先我们考虑这个问题是要求出什么?搞清楚问题是什么?其实就是pi*xi求和1到n的最大值。
//只不过他要满足指定条件,最核心需要的一个条件就是你的Wi*xi对应收益的求和不能超过M。
//所以即使我们不知道贪心算法的概念,我们也应该得出这样两个数学化的结果:
//我们所求的问题抽象为目标函数:即∑pi*xi(i为1—n)的最大值
//该求解目标函数要对应一个限制条件:即∑Wi*xi(i为1—n)
//所以自然而然就得到了贪心算法问题描述的以上的两个特点:目标函数和限制条件
//分析完问题后,我们需要去找出所谓的解,满足要求的可行解有很多,但是我们要找出问题
//要求的最大值,也就是我们所谓的最优解。
//于是自然而然的引出贪心算法最核心的逻辑思路,就是如何找到一个最优的量度标准
//也就是所谓的最优解需要满足的一种逻辑形式是什么,他的特点是什么?
//然后我们暂时抛开贪心算法的理论限制,我们就直接正常的思考这个问题
//问题的核心点(题眼)就是容量是有限的,物品可拆分,我们算出最大收益,
//其实很容易想到,既然容量有限,那么我们让单位容量下的收益最大化不就行了吗?
//例如a物品重10,价值10;b物品重20,价值15,很容易看出b的价值高,但是b的重量更高
//所以此时问题的最有量度就找到了其实就是 pi / wi.就是对应物品单位重量所得到的收益。 

package com.********;

import java.util.*;
import java.io.*;

public class Practice {

	//主函数作为程序代码测试的入口
	public static void main(String args[])
	{
		Practice p = new Practice();
		p.run();
	}
	
	//封装客户端交互的函数
	public void run()
	{
		BufferedReader br=null;
		try {
			br = new BufferedReader(new InputStreamReader(System.in));
			
			System.out.println("请输入背包的总容量:");
			int weightSum = Integer.parseInt(br.readLine());    //得到控制台的输入信息背包容量
			
			System.out.println("输入一共有多少个物品:");
			int num = Integer.parseInt(br.readLine());	//得到控制台的输入信息物品数量
			
			int [] weight_array = new int[num];	//构造保存物品重量的数组
			int [] money_array = new int[num];	//构造保存物品价值的数组
			
			for(int i=0; i<num; i++)
			{
				System.out.println("请输入第"+(i+1)+"个物品的重量和价值(空格隔开):");
				String str = br.readLine();
				String [] strc = str.split(" ");
				
				weight_array[i] = Integer.parseInt(strc[0]);	//得到用户输入的物品重量
				money_array[i] = Integer.parseInt(strc[1]);	//得到用户输入物品的价值
			}
			
			double moneyMax = getResult(weightSum, weight_array, money_array);
			System.out.println();
			System.out.println("该问题解决后可以得到的最大收益为:" + moneyMax);
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			try {
				br.close();	//注意一定要记得在finally中加入关闭io流的代码
			} catch (Exception e2) {
				e2.printStackTrace();
			} 
		}
	}
	
	//封装贪心算法函数,解决问题描述,
        //得到收益最大化的计算值,传入背包容纳重量,以及物品重量,和物品的价值
	public double getResult(int weightSum,int[] weight_array,int[] money_array)
	{
		double result=0;
		
		Devide [] devide = getWeightAndMoney(weight_array, money_array);    //得到收益比值
		
		sortDevide(devide);	//对得到的效益比值进行排序
		
		for(int i=0; i<devide.length; i++)	//遍历显示排序后的结果(从小到大)
		{
			System.out.println("第"+(i+1)+"个物品的价值和重量以及比值为:"
					+devide[i].up+"/"+devide[i].down+"="+(double)devide[i].up/devide[i].down);
		}
		
		//计算收益总值的最大化
		for(int i=devide.length-1; i>0; i--)
		{
			if(devide[i].down <= weightSum)	    //如果当前效益值代表的物品总重量小于背包的剩余容量,全部放入
			{
				result += devide[i].up;
				weightSum -= devide[i].down;
			}else	    //如果当前效益值代表的物品总重量大于背包的剩余容量,则用该物品的一部分把背包填满
			{
				//注意这一步一定是程序的循环出口,就是只有最后一次添加的时候可能出现不能整除的问题
				//这里直接采用了小数的形式,也可以直接采用分数结果的形式显示
				result += ((double)weightSum * devide[i].up / devide[i].down);
				weightSum = 0;
				break;
			}
		}
		return result;
	}
	
	//封装该贪心算法中对量度最优化的物品单位重量收益的比值数组
	public Devide[] getWeightAndMoney(int[] weight_array,int[] money_array)
	{
		Devide [] devide = new Devide[weight_array.length];		//声明比值对象数组
		
		//遍历得到每一个物品单位重量所得到的价值
		for(int i=0; i<weight_array.length; i++)
		{
			devide[i] = new Devide(money_array[i], weight_array[i]);
		}	
		
		return devide;	//返回构造好的比值分数数组
	}
	
	//对收益比值进行最简单的排序,这里排序算法不是本程序重点,只是用了简单的冒泡,time:O(n2)
	public void sortDevide(Devide[] ob)
	{
		for(int i=ob.length-1; i>0; i--)
		{
			for(int j=1; j<=i; j++)
			{
				if(devideIsBig(ob[j-1], ob[j]))		//判断是否当前位置比前一个位置的分数小
				{
					exchangeDevide(ob[j], ob[j-1]);
				}
			}
		}
	}
	
	//封装数学形式上的简单的分数类
	class Devide{
		
		public int up;		//保存该收益比值的分子,为了方便起见,并没有使用private
		public int down;	//保存该收益比值的分母
		
		//构造函数初始化,分数形式的分子和分母
		public Devide(int up,int down)
		{
			this.up = up;
			this.down = down;
		} 
	} 
	
	//封装比较两个分数的函数,判断是否第一个比第二个大
	public boolean devideIsBig(Devide ob1,Devide ob2)
	{
		if(ob1.down == ob2.down)	//分母相同,直接比较分子
		{
			return (ob1.up > ob2.up) ? true : false; 
		}else{				//分母不同,则进行同分(直接最大化同分)没有考虑最小公倍数的问题
			
			int num1_up = ob1.up * ob2.down;
			int num2_up = ob2.up * ob1.down;
			
			return (num1_up > num2_up) ? true : false; 
		}
	}
	
	//封装交换两个分数的函数
	public void exchangeDevide(Devide ob1,Devide ob2)
	{
		Devide ob3 = new Devide(ob1.up, ob1.down);	//使用一个额外的空间来实现两个对象的交换,33保存ob1的信息
		
		ob1.up=ob2.up;		//把ob2的信息赋值给ob1
		ob1.down=ob2.down;
		
		ob2.up=ob3.up;		//把ob3保存的ob1的信息赋值给ob2	
		ob2.down=ob3.down;
	}
}

显示代码的处理结果

猜你喜欢

转载自blog.csdn.net/romantic_jie/article/details/89298657