ACM基础:贪心之背包问题knapsack

2023-10-29 08:48

本文主要是介绍ACM基础:贪心之背包问题knapsack,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 一、背包问题
    • 1.描述
    • 2.难度划分
  • 二、简单:分数背包问题(Fractional knapsack)
    • 1.思路
    • 2.伪代码
    • 3.c++实现
  • 三、难:0-1背包问题(0-1 knapsack)


一、背包问题

1.描述

小偷抢劫商店,发现n件物品,物品i价值 v i v_i vi美元,重量为 w i w_i wi磅,小偷在背包中最多只能携带W磅重量,但他想尽可能多地携带贵重物品。他应该带哪些物品?

问题符号:

  • n:物品的个数
  • i:物品的序号
  • v i v_i vi:某物品的价值value
  • w i w_i wi:某物品的重量weight
  • W:背包的最大承重量

解法符号:

  • x i x_i xi:表示该物品装入背包多少。在0-1背包问题中,0(不拿)或1(拿走整件);在分数背包问题中,是个浮点数 [ 0.0 , 1.0 ] [0.0, 1.0] [0.0,1.0]0.0(不拿)或0.x(拿走部分)或1.0(拿走整件)

解法:
在满足 ∑ 1 ≤ i ≤ n w i ∗ x i ≤ W \displaystyle \sum_{1\leq i\leq n}w_i*x_i\leq W 1inwixiW的条件下,使 ∑ 1 ≤ i ≤ n v i ∗ x i \displaystyle \sum_{1\leq i\leq n}v_i*x_i 1invixi最大。

2.难度划分

依据 x i x_i xi是分数还是01,命名:

  • 简单:分数背包问题(Fractional knapsack)
  • 难:0-1背包问题(0-1 knapsack)

二、简单:分数背包问题(Fractional knapsack)

1.思路

因为可以拿走物品的部分,所以不妨将所有物品打碎当成单位物品来看,我们只需要拿走性价比(价值vi/重量wi)最高的单位物品就行,依照性价比降序来拼凑重量够整个背包。

在这里插入图片描述

2.伪代码

// v是vi数组,每个物品的价值
// w是wi数组,每个物品的重量
// W是背包的最大负重
// x是xi数组,每个物品装入背包的多少
// n是物品的数量
// 注意:对v数组和w数组有要求,按照性价比降序排列物品,即v(i)/w(i) ≥ v(i+1)/w(i+1)
GREADY-KNAPSACK(v, w, W, x, n)// 初始化都为0,哪个都不装入x ← 0// 背包的剩余容量,开始什么都不装c ← W// 遍历每个物品for i ← 1 to n// 如果物品的重量小于等于背包剩余容量,则可装入do	if w(i) ≤ c// 装入整件物品then	x(i)1// 背包剩余容量变少c ← c - w(i)// 如果装不进去,那么退出for循环else	exit// i ≤ n表示exit退出for循环(注意:for成功遍历完每个物品后,i++的最后结果是i=n+1)// 如果i ≤ n,说明两种情况,一种是整件物品装不进去但可以装进部分,另一种是背包剩余空间用尽了if i ≤ n// 装入该物品的部分:剩余空间/物品总重// 第二种情况进入没事,因为c = 0 时,x(i) = 0// 不存在c>=w(i)的情况,要不然就不会exit退出for循环then	x(i) ← c/w(i)return x

3.c++实现

#include <iostream>
#include <string.h>
using namespace std;// 物品的个数
#define n 5// 调整为性价比降序
void costPerformance_insertSort(double v[n], double w[n])
{// int c[n] = {};// for(int i = 1;i<n;i++)// 遍历第二个到最后一个,为A[i],表示要插入排序好部分的元素。// 因为第一个不用比较,所以从第二个开始for (int i = 1; i < n; i++){// 当前待插入的元素double key_v = v[i];double key_w = w[i];// 将A[i]插入到已经排序好的部分,就是A[i]前面的部分A[1...i-1]// 倒着看前面的,确定要插入的位置int j = i - 1;// 在不越界时,并且因为是降序所以是当前一个元素更小时继续while (j >= 0 && v[j] / w[j] < key_v / key_w){// 前一个元素后移v[j + 1] = v[j];w[j + 1] = w[j];// 再往前面一个j--;}// 此时才正式插入元素// 因为退出while循环时是A[j]>=key,所以插入到A[j]后面A[j+1]v[j + 1] = key_v;w[j + 1] = key_w;}
}void gready_fractional_knapsack(double v[n], double w[n], double W, double x[n])
{// 初始化都为0,哪个都不装入memset(x, 0, sizeof(x));// 背包的剩余容量,开始什么都不装double c = W;// 遍历每个物品int i;for (i = 0; i < n; i++){// 如果物品的重量小于等于背包剩余容量,则可装入if (w[i] <= c){// 装入整件物品x[i] = 1;// 背包剩余容量变少c -= w[i];}// 如果装不进去,那么退出for循环else{break;}}// i ≤ n表示exit退出for循环(注意:for成功遍历完每个物品后,i++的最后结果是i=n+1)// 如果i ≤ n,说明两种情况,一种是整件物品装不进去但可以装进部分,另一种是背包剩余空间用尽了if (i <= n){// 装入该物品的部分:剩余空间/物品总重// 第二种情况进入没事,因为c = 0 时,x(i) = 0// 不存在c>=w(i)的情况,要不然就不会exit退出for循环x[i] = c / w[i];}
}
int main()
{// 因为算性价比是小数,用double方便// 某物品的价值valuedouble v[n] = {20, 30, 65, 40, 60};// 某物品的重量weightdouble w[n] = {10, 20, 30, 40, 50};// 背包的最大承重量double W = 100;// 该物品装入背包多少double x[n] = {};// 调整为性价比降序costPerformance_insertSort(v, w);// 背包算法gready_fractional_knapsack(v, w, W, x);// 总重double my_weight = 0;// 总价值double my_value = 0;// 输出for (int i = 0; i < n; i++){my_weight += w[i] * x[i];my_value += v[i] * x[i];cout << x[i] << " ";}cout << endl;printf("[my_weight]%lf\n", my_weight);printf("[my_value]%lf\n", my_value);return 0;
}

三、难:0-1背包问题(0-1 knapsack)

咕咕咕


import java.util.*;public class Knapsack {class Commodity implements Comparable{int num;int values;int weight;public Commodity(int num, int values, int weight){this.values = values;this.weight = weight;this.num = num;}@Overridepublic int compareTo(Object o) {  //����double vdw1 = (double)this.values/this.weight;double vdw2 = (double)((Commodity)o).values/((Commodity)o).weight;if(vdw1 > vdw2) return -1;else if(vdw1 == vdw2) return 0;else return  1;}}public static void one_zero_knapsack(Commodity[] commodities, int bag, boolean[] x){int n = commodities.length;boolean[][] memory = new boolean[commodities.length+1][bag+1];int[][] c = new int[commodities.length+1][bag+1];caculate_c(commodities, c, memory, x, commodities.length,bag);}static void caculate_c(Commodity[] commodities, int[][] c, boolean[][] memory, boolean[] x, int i, int bag){if(i == 0 || bag ==0) {c[i][bag] = 0;memory[i][bag] = true;}else if(commodities[i-1].weight > bag) {if(!memory[i-1][bag]){caculate_c(commodities, c, memory, x, i-1, bag);}	c[i][bag] = c[i-1][bag];memory[i][bag] = true;}else if(i>0 && bag>=commodities[i-1].weight){if(!memory[i-1][bag-commodities[i-1].weight]){caculate_c(commodities, c, memory, x, i-1, bag-commodities[i-1].weight);}if(!memory[i-1][bag]){caculate_c(commodities, c, memory, x, i-1, bag);}int value1 = c[i-1][bag-commodities[i-1].weight] + commodities[i-1].values;int value2 = c[i-1][bag];if(value1 > value2){x[commodities[i-1].num] = true;c[i][bag] = value1;memory[i][bag] = true;}  else {c[i][bag] = value2;memory[i][bag] = true;}}}public static void fractional_knapsack(Commodity[] commodities, int bag, double[] x){Arrays.sort(commodities);int n = commodities.length;for(int i=0; i<n; ++i){if(bag - commodities[i].weight >= 0) {bag -= commodities[i].weight;x[commodities[i].num] = 1;}else{x[commodities[i].num] = (double)bag/commodities[i].weight;break;}}}/*** @param args*/public static void main(String[] args) {Knapsack ks = new Knapsack();Commodity[] commodities = {ks.new Commodity(0,20,10),ks.new Commodity(1,30,20),ks.new Commodity(2,65,30),ks.new Commodity(3,40,40),ks.new Commodity(4,60,50)};//Arrays.sort(commodities);int bag = 100;//0-1 knapsackboolean[] one_zero = new boolean[commodities.length];for(int i=0; i<commodities.length; ++i){one_zero[i] = false;}Knapsack.one_zero_knapsack(commodities, bag, one_zero);System.out.println("0-1 knapsack:");for(int i=0; i<commodities.length; ++i){System.out.println(one_zero[i]);}//fractional knapsack	double[] fra  = new double[commodities.length];Knapsack.fractional_knapsack(commodities, bag, fra);System.out.println("fractional knapsack:");for(int i=0; i<commodities.length; ++i){System.out.println(fra[i]);}}}

这篇关于ACM基础:贪心之背包问题knapsack的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/299550

相关文章

好题——hdu2522(小数问题:求1/n的第一个循环节)

好喜欢这题,第一次做小数问题,一开始真心没思路,然后参考了网上的一些资料。 知识点***********************************无限不循环小数即无理数,不能写作两整数之比*****************************(一开始没想到,小学没学好) 此题1/n肯定是一个有限循环小数,了解这些后就能做此题了。 按照除法的机制,用一个函数表示出来就可以了,代码如下

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

认识、理解、分类——acm之搜索

普通搜索方法有两种:1、广度优先搜索;2、深度优先搜索; 更多搜索方法: 3、双向广度优先搜索; 4、启发式搜索(包括A*算法等); 搜索通常会用到的知识点:状态压缩(位压缩,利用hash思想压缩)。

poj2576(二维背包)

题意:n个人分成两组,两组人数只差小于1 , 并且体重只差最小 对于人数要求恰好装满,对于体重要求尽量多,一开始没做出来,看了下解题,按照自己的感觉写,然后a了 状态转移方程:dp[i][j] = max(dp[i][j],dp[i-1][j-c[k]]+c[k]);其中i表示人数,j表示背包容量,k表示输入的体重的 代码如下: #include<iostream>#include<

hdu2159(二维背包)

这是我的第一道二维背包题,没想到自己一下子就A了,但是代码写的比较乱,下面的代码是我有重新修改的 状态转移:dp[i][j] = max(dp[i][j], dp[i-1][j-c[z]]+v[z]); 其中dp[i][j]表示,打了i个怪物,消耗j的耐力值,所得到的最大经验值 代码如下: #include<iostream>#include<algorithm>#include<

csu(背包的变形题)

题目链接 这是一道背包的变形题目。好题呀 题意:给n个怪物,m个人,每个人的魔法消耗和魔法伤害不同,求打死所有怪物所需的魔法 #include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<queue>#include<set>//#include<u>#include<map

hdu1011(背包树形DP)

没有完全理解这题, m个人,攻打一个map,map的入口是1,在攻打某个结点之前要先攻打其他一个结点 dp[i][j]表示m个人攻打以第i个结点为根节点的子树得到的最优解 状态转移dp[i][ j ] = max(dp[i][j], dp[i][k]+dp[t][j-k]),其中t是i结点的子节点 代码如下: #include<iostream>#include<algorithm

hdu1171(母函数或多重背包)

题意:把物品分成两份,使得价值最接近 可以用背包,或者是母函数来解,母函数(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v) 其中指数为价值,每一项的数目为(该物品数+1)个 代码如下: #include<iostream>#include<algorithm>

usaco 1.3 Barn Repair(贪心)

思路:用上M块木板时有 M-1 个间隙。目标是让总间隙最大。将相邻两个有牛的牛棚之间间隔的牛棚数排序,选取最大的M-1个作为间隙,其余地方用木板盖住。 做法: 1.若,板(M) 的数目大于或等于 牛棚中有牛的数目(C),则 目测 给每个牛牛发一个板就为最小的需求~ 2.否则,先对 牛牛们的门牌号排序,然后 用一个数组 blank[ ] 记录两门牌号之间的距离,然后 用数组 an

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]