# 递归
<?php
declare(strict_types=1);
function extractList($array, &$list, $temp = array())
{
if (count($temp) > 0 && !in_array($temp, $list)) {
$list[] = $temp;
}
$size = count($array);
for ($i = 0; $i < $size; $i++) {
$copy = $array;
$elem = array_splice($copy, $i, 1);
if (count($copy) > 0) {
$add = array_merge($temp, array($elem[0]));
sort($add);
extractList($copy, $list, $add);
} else {
$add = array_merge($temp, array($elem[0]));
sort($add);
if (!in_array($temp, $list)) {
$list[] = $add;
}
}
}
}
$sum = 12;
$array = array(6, 1, 3, 11, 2, 5, 12);
$list = array();
extractList($array, $list);
#Filter By SUM = $sum
$list = array_filter($list, function ($var) use ($sum) {
return (array_sum($var) == $sum);
});
echo "<pre>";
print_r(array_values($list));
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
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
# pow
<?php
$limit = 12;
$array = [1, 2, 5, 6, 7, 3];
// remove items 15 and 17 because they are great then $limit = 12
$array = array_filter($array, function ($var) use ($limit) {
return ($var <= $limit);
});
rsort($array);
// the algorithm is usable if the number of elements is less than 20 (because set_time_limit)
$num = count($array);
//The total number of possible combinations
$total = pow(2, $num);
$out = array();
// algorithm from http://r.je/php-find-every-combination.html
// loop through each possible combination
for ($i = 0; $i < $total; $i++) {
$comb = array();
// for each combination check if each bit is set
for ($j = 0; $j < $num; $j++) {
// is bit $j set in $i?
if (pow(2, $j) & $i) {
$comb[] = $array[$j];
}
}
if (array_sum($comb) == $limit) {
$out[] = $comb;
}
}
$array_map = array_map('count', $out);
array_multisort($array_map, SORT_ASC, $out);
$out = array_unique($out, SORT_REGULAR);
foreach ($out as $k => $result) {
var_dump(implode(', ', $result));
}
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
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
# 动态规划
<?php
declare(strict_types=1);
class DpTest
{
public static $list = [];
public static function main()
{
$expectValue = 3600; // 目标值
$n = 20; // 数量
self::getCouponList($n);
self::dp($expectValue, self::$list, $n);
}
private static function getCouponList($n)
{
for ($i = 1; $i < $n; $i++) {
self::$list[] = [
"expDate" => date("Y-m-d"),
"coupon" => $i * 100,
"couponId" => $i,
];
}
}
private static function dp($expectValue, $list, $n)
{
// 二维数组,记录结果,其中列代表的信息,是当前优惠券累加的值
// 比如states[5][20] == true 表示决策第六张优惠券使用或是不使用, 前6张优惠券的决策结果,存在其总和为20的组合。
$states[0][0] = true; // 哨兵,简化计算
$states[0][$list[0]["coupon"]] = true;
for ($i = 1; $i < $n; $i++) {
for ($j = 0; $j < 3 * $expectValue; $j++) {
if ($states[$i - 1][$j]) { // 第 i 张 券不用
$states[$i][$j] = true;
}
}
for ($j = 0; $j < 3 * $expectValue; $j++) {
$cow = $j + $list[$i]["coupon"];
if ($states[$i - 1][$j] && $cow < 3 * $expectValue) { // 第 i 张 券要用
$states[$i][$cow] = true;
}
}
}
$flag = false;
for ($k = $expectValue; $k < 3 * $expectValue + 1; $k++) {
if ($states[$n - 1][$k]) {
echo "最小值:{$k}";
$flag = true; // 存在最小值
break;
}
}
if (!$flag) {
return;
}
// 打印路径,即选了哪几张优惠券
for ($i = $n - 1; $i >= 0; $i--) {
$temp = $k - $list[$i]["coupon"];
if ($temp >= 0 && $states[$i][$temp]) {
echo $list[$i]["coupon"] . " \n";
$k = $k - $list[$i]["coupon"];
}
}
}
}
DpTest::main();
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
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