动态规划之挖金矿(背包问题)

1.题目描述

有一个国家,所有的国民都非常老实憨厚,某天他们在自己的国家发现了十座金矿,并且这十座金矿在地图上排成一条直线,
国王知道这个消息后非常高兴,他希望能够把这些金子都挖出来造福国民,
首先他把这些金矿按照在地图上的位置从西至东进行编号,
依次为0、1、2、3、4、5、6、7、8、9,然后他命令他的手下去对每一座金矿进行勘测,
以便知道挖取每一座金矿需要多少人力以及每座金矿能够挖出多少金子,然后动员国民都来挖金子。

题目补充说明:

1)挖每一座金矿需要的人数是固定的,多一个人少一个人都不行。国王知道每个金矿各需要多少人手.

2)每一座金矿所挖出来的金子数是固定的.

3)开采一座金矿的人完成开采工作后,他们不会再次去开采其它金矿,因此一个人最多只能使用一次.

详细分析过程看 挖金矿模型

2.测试数据

//分别为挖矿总人数,金矿数

 100 5

//下面为详细的挖第i个金矿需要的人数和该金矿对应的金子数

 77 92

 22 22

 29 87

 50 46

 99 90


3.java实现

import java.util.ArrayList;
//分析见http://www.cnblogs.com/SDJL/archive/2008/08/22/1274312.html
public class digitGold {
public int n;
int max_n=5;//总金矿数
int max_people=100;//总人数
int peopleNeed[]={77,22,29,50,99};
int gold[]={92,22,87,46,90};
//maxGold[i][j]保存了i个人挖前j个金矿能够得到的最大金子数,等于-1时表示未知
public int[][]maxGold=new int[max_people][max_n];

public void init(){
	for(int i=0;i=peopleNeed[mineNum])
			retMaxGold=gold[mineNum];
		else
			retMaxGold=0;
	}
	else if(people>=peopleNeed[mineNum]){//剩余人数大于挖该金矿人数
		int g1=getMaxGold(people-peopleNeed[mineNum],mineNum-1)+gold[mineNum];
		int g2=getMaxGold(people,mineNum-1);
		retMaxGold=Math.max(g1, g2);
	}
	else{//不挖mineNum金矿
		retMaxGold=getMaxGold(people,mineNum-1);
	}
	}
	maxGold[people][mineNum]=retMaxGold;
	return retMaxGold;
}
public static void main(String[]args){
	digitGold d=new digitGold();
	d.init();
	int s=d.getMaxGold(99,4);
	System.out.println(s);

}
以上是基于大神的c语言代码用java改写实现的,但是这里有一个问题就是当问题规模足够大时,解空间是无限的,不可能得出一个最优解。这时我们可以通过寻找局部最优解来近似逼近最好解,虽然不是实际解,但是考虑到这样可以保证在有限时间内能求出一个相对较好的解,这也是可以接受的。

4.下面我将用建模的方式来解决问题,即数学思考。(解大规模问题时十分常见)

1)建模:

设能挖出的总金子数为y

动态规划之挖金矿(背包问题)_第1张图片

g(i)代表第i个金矿挖出的金子数, x(i)代表第i 个金矿挖不挖,x(i)取值为0,1

约束(1)表示挖金矿的人数不能超过总人数这个限制

这个问题就转化为求约束条件下的解,如果问题规模很大时,我们就需要使用模拟退火等一些近似算法来求近似解。

其实这样建模的方式更好理解,但是针对小规模下存在最优解的情况下,需要访问整个解空间,这种方式是比我们上面使用的方式稍微低效一点,不过却是更好理解的。

2). 建模使用的代码如下:

package Server;

import java.util.ArrayList;

public class GoldModel {
	public int n;
	int max_n=5;//总金矿数
	int max_people=100;//总人数
	int peopleTotal;
	int peopleNeed[]={77,22,29,50,99};
	int gold[]={92,22,87,46,90};
	//最大金子数
	public int max=0;
	//保存挖取最大金子数采用的方案
	public int maxSeriale[]=new int[5];
	//生成随机的xi集合,存放随机生成的挖取的方案
	public ArrayList xs=new ArrayList();
	//得到给定条件下的总大数,相当于y函数
	public void getGold(int[]x,int g[],int[] y){
		int remax=0;
		remax=getMatrix(x,g);
		if(remax>max){
			//满足受限条件
			if(getMatrix(x,y)<=100){
				max=remax;
				maxSeriale=x;
			}
		}
	}
	//实现数组相乘
	public int getMatrix(int []x,int[] y){
		int remax=0;
		for(int i=0;i

5.总结

下面我准备将我们现有的问题规模变大,使得解空间近似与无限大,这时可以体现出使用建模来解决问题的思维方式.我们可以使用模拟退火等方法求近似解。

你可能感兴趣的:(Java应用)