改进next_permutation算法

时间:2016-04-13 16:35:59

标签: algorithm

我有以下作业:

我们有N个作品,其持续时间为:t1,t2,...,tN,其截止日期为d1,d2,...,dN。如果作品在截止日期之前没有完成,则相应地给予惩罚b1,b2,...,bN。如何完成工作,罚款将是最小的?

到目前为止,我已经编写了这段代码并且它正在运行,但我希望通过跳过不必要的排列来改进它。例如,我知道按顺序排列的工作: 1 2 3 4 5 - 将给我100分的罚款,如果我改变了命令,请让我这样说: 2 1 ..... - 它立刻给了我120点惩罚,从这一刻起我知道我不必检查从2开始的所有其余排列,我必须以某种方式跳过它们。 这是代码:

int finalPenalty = -1;
bool z = true;
while(next_permutation(jobs.begin(), jobs.end(), compare) || z)
{
    int time = 0;
    int penalty = 0;
    z = false;
    for (int i = 0; i < verseNumber; i++)
    {
        if (penalty > finalPenalty && finalPenalty >= 0)
            break;
        time += jobs[i].duration;
        if (time > jobs[i].deadline)
            penalty += jobs[i].penalty;
    }
    if (finalPenalty < 0 || penalty < finalPenalty)
    {
        sortedJobs = jobs;
        finalPenalty = penalty;
    }
    if (finalPenalty == 0)
        break;
}

我想我应该在这里做到这一点:

if (penalty > finalPenalty && finalPenalty >= 0)
            break;

但我不知道该怎么做。如果惩罚已经更高,它会在这里跳过一个排列,但它并没有跳过所有内容,它仍然会执行next_permutation。有什么想法吗?

编辑: 我使用vector,我的工作结构如下:


    struct job
    {
        int ID;
        int duration;
        int deadline;
        int penalty;
    };

从文件读取时自动给出ID,其余部分从文件中读取(例如:ID = 1,持续时间= 5,截止日期= 10,罚分= 10)

1 个答案:

答案 0 :(得分:1)

如果您打算使用STL提供的next_permutation功能,那么您无能为力。

说最后的k个数字是多余的,以便检查。如果你将使用next_permutation函数,你可以使用一个简单而低效的策略是为k调用next_permutation!时间(即最后k个元素的排列数)并且不计算他们的惩罚,因为你知道它们会更高。 (k!假设没有重复。如果你有重复,你需要采取额外的措施来计算它)这将花费你在最坏情况下的 O(k!n)操作,next_permutation has linear time complexity

让我们考虑如何改进这一点。一旦找到低效设置,声音策略可能是在再次调用next_permutation之前,按降序排序这些k个数字,以便下一个调用将有效地跳过不需要检查的排列的低效部分。请考虑以下示例。

说我们找到的方法1 2 3 4 5有100的惩罚。然后,在下一步计算2 1 3 4 5时,如果我们的方法发现我们在计算2 1后得到的罚分高于100,如果可以使用sort和自定义比较机制按降序排序3 4 5,只需跳过循环的其余部分,到达另一个next_permutation调用,这将给你2 1 4 3 5,下一个序列继续。

让我们考虑一下跳码成本。此方法需要对这些k个数字进行排序并调用next_permutation,其总时间复杂度为 O(klogk + n)。这比之前的方法有了很大的改进,它具有 O(k!n)

请参阅下文,了解我提议的方法的粗略实现,作为对现有代码的改进。我不得不使用auto类型,因为你没有提供确切的工作类型。我还排序了reversed那些k个数字,因为你没有提供比较函数,我想强调我正在做的是颠倒升序。

int finalPenalty = -1;
bool z = true;
while(next_permutation(jobs.begin(), jobs.end(), compare) || z)
{
    int time = 0;
    int penalty = 0;
    z = false;
    auto it = jobs.begin();
    for (int i = 0; i < verseNumber; i++)
    {
        time += jobs[i].duration;
        if (time > jobs[i].deadline)
        {
            penalty += jobs[i].penalty;
            if(finalPenalty >= 0 && penalty > finalPenalty)
            {
                it++; // only the remaining jobs need to be sorted in reverse
                sort(it, jobs.end(), compare);
                reverse(it, jobs.end());
                break;
            }
        }
        it++;
    }
    if (finalPenalty < 0 || penalty < finalPenalty)
    {
        sortedJobs = jobs;
        finalPenalty = penalty;
    }
    if (finalPenalty == 0)
        break;
}