背包问题knapsack的三种解法(Python 和 C)
最近研究了一下0-1背包问题,题目就不复述了,大家应该都知道的。
确切的说不是三种解法而是四种解法,下面我就一一写来。
0.枚举法
这种是最简单的一种做法,当然也是时间空间复杂度最大的方法,得到的肯定是精确的结果。若有k个背包,则只要变量2^k次方即可,程序如下。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
#include <stdio.h>
#include <stdlib.h>
int main()
{
int ca;
int mi( int , int );
int m,c,cao=0;
int weight[10],value[10];
int i,k,valuemax[100]={0},wei,val;
int x[10];
scanf ( "%d%d" ,&m,&c);
k=0;
while (m>0||c>0)
{
valuemax[cao]=0;
for (i=0;i<m;i++)
scanf ( "%d" ,&weight[i]);
for (i=0;i<m;i++)
scanf ( "%d" ,&value[i]);
for (i=0;i<mi(2,m);i++)
{
wei=0;
val=0;
ca=i;
for (k=0;k<m;k++)
{
x[k]=i%2;
i=i/2;
wei+=x[k]*weight[k];
val+=x[k]*value[k];
// k++;
}
if (wei<=c&&val>=valuemax[cao])
valuemax[cao]=val;
i=ca;
}
cao++;
scanf ( "%d%d" ,&m,&c);
}
for (i=0;i<cao;i++)
printf ( "%d\n" ,valuemax[i]);
return 0;
}
int mi( int m, int n)
{
int sum=1,i;
for (i=0;i<n;i++)
sum=sum*m;
return sum;
}
|
1.动态规划算法
这个也是一个精确的算法。动态规划,简单说来就是知道一个状态,然后根据状态转移方程得到另外一个状态。根据这两个状态间的转换,递推获得最终解。
那么我们设背包容量为C,然后每个货物的重量为Wj,每个货物的价值为Vj,一共有n个货物。下面我们分情况讨论,
① n个货物的总重量小于背包的总容量。
当然这样我们就解决问题了,就是所有的Vj之和。不要觉得这一部很没用,但凡复杂的问题就可以从简单的问题开始讨论。
② n个货物的总重量大于背包的总容量。
这时候我们可以考虑中间某一个状态,为了区分,我们设在状态i,设此时背包内所剩容量为Ci,当然此时要装的货物重量为Wi,价值为Vi.设此时背包内的可能的最大价值为Value(i, Ci)。
我们继续分解情况:
Ⅰ Wi > Ci
很简单的一种情况,此时背包内无法装下Wi的货物,所有就有Value(i, Ci) = Value(i - 1, Ci)
Ⅱ Wi <= Ci
此时可以把货物i装进去,课后死我们要不要装呢?这时候我们就可以比较一下把货物转进去会不会导致价值增高啊。也就是我们可以得到公式。Value(i, Ci) = max{Value(i - 1, Ci), Vi + V(i - 1, Ci - Wi)}。
这样我们就得到了状态方程,根据状态方程就可以轻松的把程序写出来了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
#!/usr/bin/env python
# coding=utf-8
import time
import json
def knapsack(num, cap, wei = [], val = []):
d = [[i for i in range (cap + 1 )] for j in range (num + 1 )]
wei.insert( 0 , 0 )
val.insert( 0 , 0 )
for i in range (num + 1 ):
d[i][ 0 ] = 0
for i in range (cap + 1 ):
try :
d[ 0 ][i] = 0
except :
pass
# print i
for i in range ( 1 , num + 1 ):
for j in range ( 1 , cap + 1 ):
d[i][j] = d[i - 1 ][j]
if wei[i] < = j:
temp = val[i] + d[i - 1 ][j - wei[i]]
if temp > d[i - 1 ][j]:
d[i][j] = temp
return d[num][cap]
start = time.clock()
num = 100
cap = 10000
f = open ( 'weight.txt' , 'r' )
weight = json.load(f)
f.close()
f = open ( 'value.txt' , 'r' )
value = json.load(f)
f.close()
print knapsack(num, cap, weight, value)
timeused = time.clock() - start
print '用时:' , timeused
|
2.贪心(Greedy)算法
所谓贪心,就是能装多少就装多少,显然他不是精确算法。我们可以在进行贪心算法之前对程序进行排个序,把价值与重量之比大的货物放在前面,其实是没有这个必要的,因为当数据很大的时候,是否排序对最终结果没有多少影响,不过对于数据很少的情况下还是有蛮大影响的。下面就是代码咯
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
#!/usr/bin/env python
# coding=utf-8
import time
import json
def greedydp(num, cap, weight = [], value = []):
p = [ 0 ] * num
q = [ 0 ] * num
for i in range (num):
p[i] = weight[i] / value[i]
q[i] = i
for i in range (num):
j = i + 1
while j > i and j < num:
if p[i] > p[j]:
t = q[i]
q[i] = j
q[j] = t
t = p[i]
p[i] = p[j]
p [j] = t
j + = 1
sumwei = sum = 0
for i in range (num):
for j in range (num):
if p[j] = = i:
if weight[j] + sumwei < = cap:
sum + = value[j]
sumwei + = weight[j]
return sum
start = time.clock()
num = 100
cap = 10000
f = open ( 'weight.txt' , 'r' )
weight = json.load(f)
f.close()
f = open ( 'value.txt' , 'r' )
value = json.load(f)
f.close()
print greedydp(num, cap, weight, value)
timeused = time.clock() - start
print '用时:' , timeused
|
3.基于Greedy的Rollout算法
这个算法呢,其实也是很简单的,不过在搜索相关资料的时候,却在国内很少看到相关算法,应该是国内不称作Rollout算法,而是称为启发式算法。
从启发式算法这个名字,我们可以知道这个算法是以某一算法启发而来的。也就是以某一算法作为基础,然后对其进行一些变化或者什么的使得这个算法更加的精确。当然,伴随着结果的精确肯定要付出一些代价的,世界上没有免费的午餐的,这个代价就是增加了时间复杂度,为什么呢?因为他是在贪心算法的基础上增加了一次循环。
我们这个选择货物按照编号从1到n选择,然后当选择到第t个的时候,我们考虑要不要把这个货物装进背包,如果我们看到他的重量小于此时背包的容量就装进去的话就变成了贪心算法,可是如果我们把他分成两种情况进行考虑的话,就是装与不装,然后分别进行贪心算法,然后在选择较大的情况,就ok了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
|
#!/usr/bin/env python
# coding=utf-8
import random
import json
import time
def sort2(num, weight = [], value = []):
x = [[] for i in range (num)]
#print x
for i in range (num):
x[i].append(weight[i])
x[i].append(value[i])
x[i].append( float (value[i]) / weight[i])
for i in range (num):
for j in range (i, num):
if x[i][ 2 ] < x[j][ 2 ]:
t = x[i]
x[i] = x[j]
x[j] = t
for i in range (num):
weight[i] = x[i][ 0 ]
value[i] = x[i][ 1 ]
return weight, value
def greedy(num, cap, weight = [], value = []):
sumwei = sum = 0
for i in range (num):
if weight[i] + sumwei < = cap:
sum + = value[i]
sumwei + = weight[i]
return sum
def rollout(num, cap, weight = [], value = []):
leftweight = [i for i in weight]
leftvalue = [i for i in value]
notchoose = [i for i in range (num)]
choose = []
choosenum = 1
choosedig = - 1
choosevalue = 0
summax = 0
valueatlast = 0
i = 0
for t in range (num):
flag = - 1
for i in notchoose:
flag + = 1
if weight[i] < = cap:
cap - = weight[i]
choose.append(i)
leftvalue.pop(flag)
leftweight.pop(flag)
tempsum = choosevalue + value[i] + greedy(num - len (choose), cap, leftweight, leftvalue)
leftweight.insert(flag, weight[i])
leftvalue.insert(flag, value[i])
cap + = weight[i]
choose.pop()
if tempsum > = summax:
summax = tempsum
choosedig = i
if choosedig > = 0 :
# print choosedig
choose.append(choosedig)
notchoose.remove(choosedig)
leftvalue.remove(value[choosedig])
leftweight.remove(weight[choosedig])
cap - = weight[choosedig]
choosevalue + = value[choosedig]
choosedig = - 1
return summax
start = time.clock()
num = 100
cap = 10000
f = open ( 'weight.txt' , 'r' )
weight = json.load(f)
f.close()
f = open ( 'value.txt' , 'r' )
value = json.load(f)
f.close()
print rollout(num, cap, weight, value)
timeused = time.clock() - start
print '用时:' , timeused
|
总结:我们同过生成100组随机数据对三种算法进行了测试,结果如下:
测试数据为100个货物,背包容量为10000,采用随机数生成1----500的数作为重量,用程序运行结束时的时间与开始时的时间之差作为计时标准。 则结果如下:
算法名称 |结果 | 耗时 |结果2 |耗时2
Greedy |9110 |0.00157397626884 |10221 |0.0016819459503
Rollout |14463 |0.0354518243797|13457|0.0590536683212
动态规划 |14738 |0.492143871448 |14737|0.48608771654
生成随机数的算法还是很简单的。
详细代码级结果可参见我的Github: https://github.com/zhangzhishan/knapsack
背包问题knapsack的三种解法(Python 和 C)相关推荐
- Python实现阶乘的三种解法
Python实现阶乘的三种解法 问题描述 输入一个正整数n,输出n!的值. 其中n!=123*-*n. 算法描述 n!可能很大,而计算机能表示的整数范围有限,需要使用高精度计算的方法.使用一个数组A来 ...
- [简单题]自定义取余(三种解法)C++实现
题目链接: 点击打开原题链接 题目意思,就是标题意思. 第一种解法:(加法迭代)用加法来模拟这个(17行代码) int mod256WithoutMod(int number) {if (number ...
- jsp判断字符串相等_最长回文字符串三种解法
先解释一下什么是回文字符串,比如说字符串"aba",无论是从先往后读取还是从后往前读取,结果都是一样的.当给定很长的字符串时,如何快速获取到最长的回文字符串,这也是大厂比较常见的算 ...
- 【三种解法】剑指 Offer 06. 从尾到头打印链表【附完整可运行代码】
立志用最少的代码做最高效的表达 输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回). 示例 1: 输入:head = [1,3,2] 输出:[2,3,1] 限制: 0 <= 链表 ...
- 【三种解法】Not so Mobile UVA - 839_19行代码AC
立志用最少的代码做最高效的表达 Before being an ubiquous communications gadget, a mobile was just a structure made o ...
- 【LeetCode】1. 盛最多水的容器:C#三种解法
题目:https://leetcode-cn.com/problems/container-with-most-water/ 盛最多水的容器 难度:中等 给你 n 个非负整数 a1,a2,...,an ...
- 【ArcGIS遇上Python】三种利用Python批量处理地理数据的方法——以栅格数据投影转换为例
时至今日,笔者已经总结了三种用Python语言结合ArcGIS10.x提供的接口去批量处理地理空间数据的方法.即: 1. 用IDLE.Python Tools for Visual Studio等去编 ...
- 一只青蛙跳向三个台阶_青蛙跳台阶问题的三种解法
题目:一只青蛙一次可以跳 1 级台阶,也可以跳 2 级.求该青蛙跳上一个 n 级的台阶总共有多少种跳法. 这道题还被 ITEye 放在了博文视点杯有奖答题活动里面. 我提供三种解法. 1.递归求解: ...
- python判断字符串合法,详解Python判定IP地址合法性的三种方法 python中判断一个字符串是否是IP地址...
html 中 鼠标放在标签上会显示小手状,其它标签在其他标签上,美工给加了一些样式,鼠标放上去也显示小手状.有哪位大手状样式 有什么不懂的前端问题可以去菜鸟驿站.全都是泡沫,只一刹的花火,所谓的友情, ...
最新文章
- 从39个kaggle竞赛中总结出来的图像分割的Tips和Tricks(附资源)
- elementui的el-tree第一次加载无法展开和选中的问题
- ci框架去除index.php,ci框架如何隐藏index.php
- duilib基本布局学习(模仿百度网盘)及绑定按钮事件
- C++smallest circle 获取外接给定点集的最小圆的中心和半径算法(附完整源码)
- Python3.7模块之re
- 【Lintcode】018.Subsets II
- Taro+react开发(46)taro中环境判断
- ftk学习记(进度条篇)
- 持有至少百万美元钱包本周增至66,540,增长了150%
- 协议(protocol)与网络协议
- Android Studio快捷键-mac版
- 如何才能写好代码,做到以下几点即可
- iOS runtime实用篇解决常见Crash
- 镁光ssd管理工具 linux,解决BIOS找不到SSD,镁光SSD固态硬盘升级固件方法
- sicilyOJ 09广东省赛重现H Westward Journey Online(线段树)
- arcgis去除遥感影像黑边
- MATLAB积分计算与积分变换
- h5滚动时侧滑出现_HTML5移动端触摸事件以及滑动翻页效果
- 编程时常犯的几个错误、应注意的事项以及技巧
热门文章
- RTX51Tiny 学习笔记(三)
- (十)汇编语言——CALL和RET指令
- 强到离谱!阿里P8前端面试全套笔记,即将删除~
- CNN实现剪刀石头布手势识别(python)
- Oracle Developer 论坛
- _C.cpython-36m-x86_64-linux-gnu.so: undefined symbol: _ZN6caffe28TypeMeta21_typeMetaDataInstanceISt7
- VNCServer,SSH Secure Shell Client,window远程控制linux
- 计算机英语mask,英语单词mask怎么读,mask的音标是什么,mask是什么意思 - 音标网
- 【解决回车键出现乱码的问题】禁用Enter键触发form表单提交的问题 ,同时启用保留textarea换行操作
- 源码管理工具VSN的安装与使用--eclipse