【问题标题】:running fast dynamic programming algorithm to get all the answers运行快速动态规划算法以获得所有答案
【发布时间】:2018-08-05 07:06:50
【问题描述】:

我如何运行快速动态规划算法来获得所有可能的答案。 假设我们有 20 个条目,它只显示 1 行最佳答案,我希望它一直运行并显示其他条目,直到所有条目都显示出来,并且不允许重复。 太感谢了。真的很感激。 这是代码:

#include <iostream>
#include <set>
#include <vector>
#include <map>
#include <utility>

using namespace std;

float W ,N; //N = olcu sayisi, W = profil boyu
vector<float> numbers; //stores the set of numbers

pair<float, multiset<float>> calc(float i, float j) //returns closest sum and best subset of the first i numbers for the target value j
{
    static map<pair<float, float>, pair<float, multiset<float>>> dp; //stores results to avoid repeated calculations

    pair<float, float> p(i, j); //pair for convenience

    if(i == 0) //base case
    {
        return make_pair(0, multiset<float>(
                                      {}));
    }

    auto findResult = dp.find(p);

    if(findResult != dp.end()) //check if already calculated
    {
        return findResult->second;
    }

    auto temp1 = calc(i - 1, j); //compute result if not using number

    if(numbers[i - 1] > j) //if current number is too big
    {
        return temp1;
    }

    auto temp2 = calc(i - 1, j - numbers[i - 1]); //compute result if using number
    temp2.first += numbers[i - 1];
    temp2.second.insert(numbers[i - 1]);

    pair<float, multiset<float>> result;
    if(temp1.first != temp2.first) //compare results and choose best
    {
        result = temp1.first > temp2.first ? temp1 : temp2;
    }
    else
    {
        result = temp1.second.size() < temp2.second.size() ? temp1 : temp2;
    }
    dp[p] = result;

    return result;
}

int main()
{
    cout << "sineklik sayisi: ";
    cin >> N;
    N = 2 * N;
    cout << "Profil olcusu: ";
    cin >> W;
    numbers.reserve(N); //avoid extra reallocations

    cout << "Olculeri giriniz: ";
    for(int i = 0; i < N; i++) //input loop
    {
        float temp;
        cin >> temp;
        numbers.push_back(temp);
    }

    pair<float, multiset<float>> result = calc(N, W); //calculate

    //output below
    cout << "The best possible sum is " << result.first << " Left behind is " << W - result.first << ", obtained using the set of numbers {";
    if(result.second.size() > 0)
    {
        cout << *result.second.begin();
        for(auto i = ++result.second.begin(); i != result.second.end(); i++)
        {
            cout << ", " << *i;
        }
    }
    cout << "}.\n";
}

【问题讨论】:

  • 所有可能的答案都是一个非常棘手的问题。道格拉斯·亚当斯(Douglas Adams)为此工作了很长时间,他能做到的最好成绩是 42 岁
  • 那么我们必须取下一个最好的,显示最好的条目总和就可以了,但我们必须显示所有条目,不要重复,例如:条目:10,20,30, 40,50,60 目标总和 : 70 答案 : {10,60},{50,20},{30,40} ----- 现在它只显示 {10,60} 以供回答真诚感谢各位跨度>
  • @user4581301 你能帮帮我吗?欣赏它。
  • 我正在研究它,不需要其他问题。如果不阅读代码,大多数人甚至都不知道您在说什么。
  • @FeiXiang 非常感谢你,我知道我可以依靠你,衷心感谢。

标签: c++ optimization mathematical-optimization


【解决方案1】:

编辑:这是我较早的答案之一。那时我还没有那么好,现在我知道有一个更简单、更快、内存消耗更少的解决方案来解决这个问题。如果我们在只存储最接近的可能和的表中自下而上计算 DP,我们可以稍后使用我们计算的表值递归地重建子集。

一种解决方案,输出总和等于最大可能总和但不大于目标值且包含尽可能少的数字的所有数字集:

#include <iostream>
#include <set>
#include <vector>
#include <map>
#include <utility>

using namespace std;

int N, W; //N = number of numbers, W = target sum
vector<int> numbers; //stores the set of numbers

pair<int, set<multiset<int>>> calc(int i, int j) //returns closest sum and best subset of the first i numbers for the target value j
{
    static map<pair<int, int>, pair<int, set<multiset<int>>>> dp; //stores results to avoid repeated calculations
    pair<int, int> p(i, j); //pair for convenience
    if(i == 0) //base case
    {
        set<multiset<int>> temp;
        temp.emplace();
        return make_pair(0, temp);
    }
    auto findResult = dp.find(p);
    if(findResult != dp.end()) //check if already calculated
    {
        return findResult->second;
    }
    auto temp1 = calc(i - 1, j); //compute result if not using number
    if(numbers[i - 1] > j) //if current number is too big
    {
        return temp1;
    }
    pair<int, set<multiset<int>>> temp2 = calc(i - 1, j - numbers[i - 1]), newtemp2; //compute result if using number
    newtemp2.first = temp2.first + numbers[i - 1];
    for(const auto k : temp2.second)
    {
        multiset<int> temp = k;
        temp.insert(numbers[i - 1]);
        newtemp2.second.insert(temp);
    }
    pair<int, set<multiset<int>>> *result;
    if(temp1.first != newtemp2.first) //compare results and choose best
    {
        result = temp1.first > newtemp2.first ? &temp1 : &newtemp2;
    }
    else if(temp1.second.begin()->size() != newtemp2.second.begin()->size())
    {
        result =
                temp1.second.begin()->size() < newtemp2.second.begin()->size() ? &temp1 : &newtemp2;
    }
    else
    {
        temp1.second.insert(newtemp2.second.begin(), newtemp2.second.end());
        result = &temp1;
    }
    dp.insert(make_pair(p, *result));
    return *result;
}

int main()
{
    cout << "Enter the number of numbers: ";
    cin >> N;
    cout << "Enter target sum: ";
    cin >> W;
    numbers.reserve(N); //avoid extra reallocations
    cout << "Enter the numbers: ";
    for(int i = 0; i < N; i++) //input loop
    {
        int temp;
        cin >> temp;
        numbers.push_back(temp);
    }
    pair<int, set<multiset<int>>> result = calc(N, W); //calculate
    //output below
    cout << "The best possible sum is " << result.first << ", which can be obtained using a set of "
            << result.second.begin()->size() << " numbers " << result.second.size()
            << " different ways:\n";
    for(const auto &i : result.second)
    {
        cout << '{';
        if(i.size() > 0)
        {
            cout << *i.begin();
            for(auto j = ++i.begin(); j != i.end(); ++j)
            {
                cout << ", " << *j;
            }
        }
        cout << "}\n";
    }
}

此解决方案不允许数字在输出中出现的次数超过它在输入中出现的次数。如果您只想允许一个数字在输出中出现一次,即使它在输入中出现多次,请将 multiset numbersleft 更改为一个集合。

#include <iostream>
#include <set>
#include <vector>
#include <map>
#include <utility>

using namespace std;

int N, W; //N = number of numbers, W = target sum
vector<int> numbers; //stores the set of numbers

pair<int, set<multiset<int>>> calc(int i, int j) //returns closest sum and best subset of the first i numbers for the target value j
{
    static map<pair<int, int>, pair<int, set<multiset<int>>>> dp; //stores results to avoid repeated calculations
    pair<int, int> p(i, j); //pair for convenience
    if(i == 0) //base case
    {
        set<multiset<int>> temp;
        temp.emplace();
        return make_pair(0, temp);
    }
    auto findResult = dp.find(p);
    if(findResult != dp.end()) //check if already calculated
    {
        return findResult->second;
    }
    auto temp1 = calc(i - 1, j); //compute result if not using number
    if(numbers[i - 1] > j) //if current number is too big
    {
        return temp1;
    }
    pair<int, set<multiset<int>>> temp2 = calc(i - 1, j - numbers[i - 1]), newtemp2; //compute result if using number
    newtemp2.first = temp2.first + numbers[i - 1];
    for(const auto k : temp2.second)
    {
        multiset<int> temp = k;
        temp.insert(numbers[i - 1]);
        newtemp2.second.insert(temp);
    }
    pair<int, set<multiset<int>>> *result;
    if(temp1.first != newtemp2.first) //compare results and choose best
    {
        result = temp1.first > newtemp2.first ? &temp1 : &newtemp2;
    }
    else if(temp1.second.begin()->size() != newtemp2.second.begin()->size())
    {
        result =
                temp1.second.begin()->size() < newtemp2.second.begin()->size() ? &temp1 : &newtemp2;
    }
    else
    {
        temp1.second.insert(newtemp2.second.begin(), newtemp2.second.end());
        result = &temp1;
    }
    dp.insert(make_pair(p, *result));
    return *result;
}

int main()
{
    cout << "Enter the number of numbers: ";
    cin >> N;
    cout << "Enter target sum: ";
    cin >> W;
    numbers.reserve(N); //avoid extra reallocations
    cout << "Enter the numbers: ";
    for(int i = 0; i < N; i++) //input loop
    {
        int temp;
        cin >> temp;
        numbers.push_back(temp);
    }
    pair<int, set<multiset<int>>> result = calc(N, W); //calculate
    //output below
    cout << "The best possible sum is " << result.first << ", which can be obtained using sets of "
            << result.second.begin()->size() << " numbers:\n";
    multiset<int> numbersleft;
    numbersleft.insert(numbers.begin(), numbers.end());
    for(const auto &i : result.second)
    {
        bool good = true;
        for(const int &j : i)
        {
            if(numbersleft.find(j) == numbersleft.end())
            {
                good = false;
                break;
            }
        }
        if(good)
        {
            for(const int &j : i)
            {
                numbersleft.erase(j);
            }
            cout << '{';
            if(i.size() > 0)
            {
                cout << *i.begin();
                for(auto j = ++i.begin(); j != i.end(); ++j)
                {
                    cout << ", " << *j;
                }
            }
            cout << "}\n";
        }
    }
}

【讨论】:

  • 我已经修改了你的代码,答案让我告诉你它是如何反应的,对于 30 个条目,结果是这样的:最好的总和是 598 留下 0,可以使用 set 获得数字:{19.8, 57.8, 67.8, 69.8, 109.2, 114.8, 158.8} 最好的总和是 598 留下 0,可以使用以下数字集获得:{19.8, 57.8, 67.8, 69.8, 110.2, 113.8 , 158.8} 最好的总和是 598 留在 0 后面,可以使用一组数字获得:{19.8, 57.8, 67.8, 69.8, 110.8, 113.2, 158.8}
  • {19.8, 57.8, 67.8, 69.8, 109.2, 114.8, 158.8} {19.8, 57.8, 67.8, 69.8, 110.2, 113.8, 158.8} {19.8, 57.8, 2.8, 67..8, 11.3, 67.8, 69.8 ,158.8} {19.8,57.8,67.8,69.8,110.8,123.2,148.8} {19.8,57.8,67.8,69.8,110.8,133.2,138.8} {19.8,57.8,67.8,69.8,111.2,112.8,158.8} {19.8 ,57.8,67.8,69.8,111.8,112.2,158.8,67.8,69.8,111.8,122.2,148.8,67.8,69.8,113.8,120.2,148.8,120.2,148.8} {19.8,57.8,67.8, 77.8, 109.2, 116.8, 148.8} {19.8, 57.8, 67.8, 77.8, 110.2, 115.8, 148.8} {19.8, 57.8, 67.8, 77.8, 111.2, 114.8, 148.8} 等等
  • 据我所知,这显示了所有答案,每个数字序列我们只需要一个答案,例如假设我们有 [10,20,30,40,50,60, 70,80,90] 对于条目,目标总和是 100,答案应该是:{10,90}{20,80}{30,70}{40,60}{50},但现在它的行为是这样的:{ 10,90}{10,20,70}{10,30,60}{10,40,50}等等……,真诚等待你的好意回复,比你好多了
  • 我的鹿朋友,如果我们可以编写代码来计算给定数字的最佳总和而不重复数字,我们就完成了,总和应该尽可能接近给定的最大值,非常感谢,等待您的回复
  • 你能给我你的代码吗?看起来这可能是您的错误,也可能与浮点错误有关。我从来没有用浮点数测试过系统。
猜你喜欢
  • 1970-01-01
  • 2012-08-08
  • 2012-03-26
  • 2013-11-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多