我猜想,这样的算法已经存在了。
我有两个(或更多,但对于这个问题,两个就足够了)限制,例如,limit_a=20 limit_b=18。 然后我有一些(a,b)对,例如。
(5,5),(3,3),(4,2),(1,7),(3,2),(5,9),(7,4)
答案应该是5。 解的例子:(7,4),(3,2),(1,7),(4,2),(3,3)
我需要选择尽可能多的对,使得所有“a”元素的和小于或等于limit_a,并且类似于“b”。 我以为这是2D背包问题,但它不是。 我最好的“解决方案”是检查这些对的列表的所有排列,并检查和。 例如,上面的一个是可以的,但当然不是更大的一个。 我的C++代码:
#include <iostream>
#include <algorithm>
#include <utility>
#include <vector>
using namespace std;
int main()
{
int limit_a = 20;
int limit_b = 18;
vector<pair<int, int>> vect;
vect.push_back(make_pair(5, 5));
vect.push_back(make_pair(3, 3));
vect.push_back(make_pair(4, 2));
vect.push_back(make_pair(1, 7));
vect.push_back(make_pair(3, 2));
vect.push_back(make_pair(5, 9));
vect.push_back(make_pair(7, 4));
int how_many_max = 0;
do {
int copy_a = limit_a;
int copy_b = limit_b;
int how_many = 0;
for ( vector<pair<int,int>>::const_iterator it = vect.begin(); it != vect.end(); it++){
copy_a -= it->first;
copy_b -= it->second;
if((copy_a < 0) || (copy_b < 0)) {
break;
}
how_many++;
}
if (how_many > how_many_max) how_many_max = how_many;
} while(next_permutation(vect.begin(), vect.end() ));
cout << how_many_max;
return 0;
}
示例:
int limit_a = 30;
int limit_b = 80;
std::vector<std::pair<int, int>> vect = {{37, 20}, {90, 45}, {76, 33}, {3, 93}, {66, 71}, {48, 21}, {8, 28}, {24, 83}, {99, 13}, {42, 52}, {81, 15}, {2, 38}, {7, 19}, {32, 65}, {70, 85}, {12, 82}, {61, 6}, {60, 31}, {46, 34}, {43, 62}, {41, 78}, {64, 80}, {88, 86}, {77, 16}, {44, 100}, {92, 57}, {40, 53}, {9, 56}, {68, 67}, {23, 11}, {35, 30}, {69, 84}, {75, 27}, {87, 26}, {50, 36}, {79, 73}, {4, 91}, {17, 98}, {51, 29}, {25, 95}, {14, 55}, {10, 58}, {54, 49}, {97, 63}, {59, 72}, {1, 39}, {18, 22}, {94, 74}, {96, 5}, {47, 89}
应给出3
:({2,38},{7,19},{18,22})
自下而上动态编程的朴素搜索空间似乎是O(n*limit_a*limit_b)
,但这可能会得到更多的特性,这取决于输入,因此我们可能更倾向于记忆递归。
null
function f(pairs, a, b, i=0, memo={}){
if (i == pairs.length)
return 0;
const key = String([i, a, b]);
if (memo.hasOwnProperty(key))
return memo[key];
// Skip this pair
let best = f(pairs, a, b, i+1, memo);
const [l, r] = pairs[i];
// Maybe include this pair
if (l <= a && r <= b)
best = Math.max(best, 1 + f(pairs, a-l, b-r, i+1, memo));
return memo[key] = best;
}
var pairs = [
[5, 5], [3, 3], [4, 2],
[1, 7], [3, 2], [5, 9], [7, 4]
];
console.log(f(pairs, 20, 18));
这是一个2D背包问题,只是利润都是1。 通常的方法是生成与剪枝交错的子集,其中一个部分子解明显支配另一个。
下面的一些快速代码,为了方便起见,使用排序而不是合并。
#include <algorithm>
#include <iostream>
#include <tuple>
#include <utility>
#include <vector>
int main() {
int limit_a = 20;
int limit_b = 18;
std::vector<std::pair<int, int>> vect = {{5, 5}, {3, 3}, {4, 2}, {1, 7},
{3, 2}, {5, 9}, {7, 4}};
limit_a = 30;
limit_b = 80;
vect = {{37, 20}, {90, 45}, {76, 33}, {3, 93}, {66, 71}, {48, 21}, {8, 28},
{24, 83}, {99, 13}, {42, 52}, {81, 15}, {2, 38}, {7, 19}, {32, 65},
{70, 85}, {12, 82}, {61, 6}, {60, 31}, {46, 34}, {43, 62}, {41, 78},
{64, 80}, {88, 86}, {77, 16}, {44, 100}, {92, 57}, {40, 53}, {9, 56},
{68, 67}, {23, 11}, {35, 30}, {69, 84}, {75, 27}, {87, 26}, {50, 36},
{79, 73}, {4, 91}, {17, 98}, {51, 29}, {25, 95}, {14, 55}, {10, 58},
{54, 49}, {97, 63}, {59, 72}, {1, 39}, {18, 22}, {94, 74}, {96, 5},
{47, 89}};
std::vector<std::vector<std::pair<int, int>>> frontier = {
{{limit_a, limit_b}}};
for (auto [a, b] : vect) {
frontier.push_back({});
for (std::size_t how_many = frontier.size() - 1; how_many > 0; how_many--) {
std::vector<std::pair<int, int>> &level = frontier[how_many];
for (auto [residual_a, residual_b] : frontier[how_many - 1]) {
if (residual_a >= a && residual_b >= b)
level.push_back({residual_a - a, residual_b - b});
}
if (level.empty())
continue;
std::sort(level.begin(), level.end(),
std::greater<std::pair<int, int>>());
auto output = level.begin();
auto input = output;
for (++input; input != level.end(); ++input) {
if (std::tie(input->second, input->first) >
std::tie(output->second, output->first))
*++output = *input;
}
level.erase(++output, level.end());
if ((false)) {
for (auto [residual_a, residual_b] : level) {
std::cout << residual_a << ',' << residual_b << ' ';
}
std::cout << '\n';
}
}
}
std::size_t how_many_max = frontier.size() - 1;
while (frontier[how_many_max].empty())
how_many_max--;
std::cout << how_many_max << '\n';
}
在更高的维度中,剪枝变得更加复杂。 维度的诅咒也起作用,因为支配关系变得稀疏。 在这里,整数编程可能是一个更好的解决方案。