背包问题(Knapsack problem)采用动态规划求解
问题说明:
假设有一个背包的负重最多可达8公斤,而希望在背包中装入负重范围内可得之总价物
品,假设是水果好了,水果的编号、单价与重量如下所示:
0
李子
4KG
NT$4500
1
苹果
5KG
NT$5700
2
橘子
2KG
NT$2250
3
草莓
1KG
NT$1100
解法背包问题是关于最佳化的问题,要解最佳化问题可以使用「动态规划」 (Dynamicprogramming) ,从空集合开始,每增加一个元素就先求出该阶段的最佳解,直到所有的元素加入至集合中,最后得到的就是最佳解。
下面我们看下代码:
/* 问题: 假设有一个背包的负重最多可达8公斤,而希望在背包中装入负重范围内可得之总价物品 算法说明: 采用动态规划,在当前阶段求解出最好的解,如此反复 日期:2013/8/18 张威 */ #include <iostream> #include <time.h> using namespace std; #define MAXSIZE 8 //定义全局变量 char name[5][5] = {"李子","苹果","橘子","草莓","甜瓜"};//水果名称 int wight[5] = {4,5,2,1,6};//单个水果所占斤数 int price[5] = {4500,5700,2250,1100,6700};//单个水果的价值 int perkg_price[5];//每斤水果的价钱 int perkg_num[5] = {0,1,2,3,4}; void GetNmae(int num) { for (int i = 0;i <= 4;i++) { cout<<name[num][i]; } } void GetBestAnswer(int currentwigh) { //判断递归终止条件 if (currentwigh >= MAXSIZE) { cout<<"包裹已经满了,无法再装进东西"<<endl; } else { //check用来表证到底剩下来的物品里面还有没有能装进去背包里的 bool check = true; int i = 0; for (;i <= 4;i++) { //若是没有进入到这个条件内,说明剩下来的物品的重量都超过了背包剩余重量,到此结束.否则i就代表当前所能选中的最优解 if (wight[perkg_num[i]] <= MAXSIZE-currentwigh) { check = false; break; } } if (check == true) { cout<<"已经装不进去任何水果了"<<endl; } else { //得到最优解,并且将当前重量增加,进入下一次递归 currentwigh += wight[perkg_num[i]]; cout<<"购买了"; GetNmae(perkg_num[i]); cout<<endl; GetBestAnswer(currentwigh); } } } int main() { //计算出每斤水果的价钱,便于动态规划时求出当前最佳解 for (int i = 0;i <= 4;i++) { perkg_price[i] = price[i] / wight[i]; } //对perkg_num进行排序,同时保证单价和perkg_num之间的一一对应关系.即两个数组要同时变化 //采用的是冒泡排序,在元素进行交换时perkg_num和perkg_price同时变化 for (int i = 0;i <= 3;i++) { for (int j = i;j <= 3;j++) { if (perkg_price[j] < perkg_price[j+1]) { int temp1 = perkg_price[j]; int temp2 = perkg_num[j]; perkg_price[j] = perkg_price[j+1]; perkg_price[j+1] = temp1; perkg_num[j] = perkg_num[j+1]; perkg_num[j+1] = temp2; } } } //开始计算求解 GetBestAnswer(0); return 0; }
在这里,算法的主要思想有两个:1.通过冒泡排序得到一个单价表,并将物品的ID与之配对起来.这样我们在每次的递归中通过ID找到物品的相应属性,筛选出当前步骤的最优解出来
2.通过递归,传递当前的重量,得到还剩余的重量,根据前面的单价表,筛选出可选的最优解,然后将重量变化进入下一次递归.
这是最大空间为8的运行结果: 这是最大空间为29的运行结果:
下面附上指导书上面的代码:
#include <stdio.h> #include <stdlib.h> #define LIMIT 8 // 重量限制 #define N 5 // 物品种类 #define MIN 1 // 最小重量 struct body { char name[20]; int size; int price; }; 背 包 负 重 1 2 3 4 5 6 7 8 valu e 110 0 225 0 335 0 450 0 570 0 680 0 795 0 905 0 item 3 2 3 0 1 3 2 3 背 包 负 重 1 2 3 4 5 6 7 8 valu e 110 0 225 0 335 0 450 0 570 0 680 0 795 0 905 0 item 3 2 3 0 1 3 2 3 typedef struct body object; int main(void) { int item[LIMIT+1] = {0}; int value[LIMIT+1] = {0}; int newvalue, i, s, p; object a[] = {{"李子", 4, 4500}, {"苹果", 5, 5700}, {"橘子", 2, 2250}, {"草莓", 1, 1100}, {"甜瓜", 6, 6700}}; for(i = 0; i < N;i++) { for(s = a[i].size; s <= LIMIT;s++) { p = s - a[i].size; newvalue = value[p] + a[i].price; if(newvalue > value[s]) {// 找到阶段最佳解 value[s] = newvalue; item[s] = i; } } } printf("物品\t价格\n"); for(i = LIMIT;i >= MIN;i = i - a[item[i]].size) { printf("%s\t%d\n", a[item[i]].name, a[item[i]].price); } printf("合计\t%d\n", value[LIMIT]); return 0; } Java class Fruit { private String name; private int size; private int price; public Fruit(String name,int size, int price){ this.name = name; this.size = size; this.price = price; } public String getName(){ return name; } public int getPrice(){ return price; } public int getSize() { return size; } } public class Knapsack { public static void main(String[] args){ final int MAX = 8; final int MIN = 1; int[] item = new int[MAX+1]; int[] value = new int[MAX+1]; Fruit fruits[] = { new Fruit("李子", 4, 4500), new Fruit("苹果", 5, 5700), new Fruit("橘子", 2, 2250), new Fruit("草莓", 1, 1100), new Fruit("甜瓜", 6, 6700)}; for(int i = 0; i < fruits.length;i++) { for(int s = fruits[i].getSize(); s <= MAX;s++){ int p = s - fruits[i].getSize(); int newvalue = value[p] + fruits[i].getPrice(); if(newvalue > value[s]) {// 找到阶段最佳解 value[s] = newvalue; item[s] = i; } } } System.out.println("物品\t价格"); for(int i = MAX; i >= MIN; i = i - fruits[item[i]].getSize()) { System.out.println(fruits[item[i]].getName()+ "\t" + fruits[item[i]].getPrice()); } System.out.println("合计\t" + value[MAX]); } }
我居然没想到使用结构体,失策失策,都没用什么高级点的数据结构,看起来貌似很复杂的样子.明天再看