查找序列,以便最早完成事件

时间:2017-05-06 16:02:22

标签: c++ algorithm dynamic-programming

这是来自informatica奥林匹克运动会的一个问题,我有时会尝试解决这个问题。这对我来说很重要,因为这包含了我在很多问题中看到的潜在的基本问题。

考虑到N个公民参加的活动,他们必须在一台电脑上进行编程,吃巧克力,然后吃甜甜圈。时间,每个任务的公民所需的时间作为输入。每个公民都必须按顺序完成任务,即第一个程序然后吃巧克力,然后吃甜甜圈。任何数量的人一次都可以吃巧克力或甜甜圈,但由于计算机是一个人,每次只能有1个人。有一次,他完成了他将转移到巧克力,下一个人应该编程。任务是找到公民被派出去编程的顺序,以便事件在最短的时间内结束,这次是输出。

我使用这种方法解决了这个问题: 如果我从ith公民开始,那么如果我找到时间(tn-1)则剩下n-1公民,然后tn = max((ni [0] + ni [1] + ni [2]),ni [0] + TN-1)。例如。: 18 7 6 23 10 27 20 9 14

然后18 + 7 + 6,18 + 23 + 10 + 27,18 + 23 + 20 + 9 + 14,最大值为84但是如果你从23开始那么时间将是74而不是。

我实现了这种方法,我的代码在这里呈现。但是,对于我的方法,复杂度是O(n!)。我可以看到潜在的重复子问题,所以我可以使用DP方法。但问题是我需要将每个列表i的时间值存储到j,以便它可以从i到j的任何k开始,依此类推。这个存储过程将再次复杂,需要n!存储。如何,解决这个问题和类似的问题?

这是关于我的方法的程序:

#include <iostream>
#include <vector>
#include <climits>

int min_time_sequence(std::vector<std::vector<int> > Info, int N)
{
    if (N == 0) return 0;
    if (N == 1)
    {
        int val = Info[0][0] + Info[0][1] + Info[0][2];
        return val;
    }
    std::vector<std::vector<int> > tmp = Info;
    int mn = INT_MAX;
    for (int i = 0; i < N; ++i)
    {
        //prepare new list
        tmp.erase(tmp.begin()+i);
        int mn = min_time_sequence(tmp, N-1);
        int v1 = Info[i][0] + mn;
        int v2 = Info[i][0] + Info[i][1] + Info[i][2];
        int larger = v1 > v2 ? v1 : v2;
        if (mn > larger) mn = larger;
    }
    return mn;
}

int main()
{
    int N;
    std::cin>>N;
    std::vector<std::vector<int> > Info;
    //input
    for (int i = 0; i < N; ++i)
    {
        std::cin>>Info[i][0];
        std::cin>>Info[i][1];
        std::cin>>Info[i][2];
    }
    int mx = 0;
    if (N > 0)
        mx = min_time_sequence(Info, N);
    std::cout<<mx<<std::endl;
    return 0;
}

1 个答案:

答案 0 :(得分:0)

由于您要求使用常规技术,您可能需要查看贪婪算法,即重复优化下一个选择的算法。在这种情况下,可能是剩余的人将花费最长的总时间(三次总和)来接下来进行编程,因此他或她将很快完成进食,并且没有人在晚些时候开始会花更多的时间。

如果这样的算法是最优的,程序可以简单地按照递减的顺序对列表进行排序,这需要O( N log N )时间。

但是,您需要证明您的解决方案是有效的。实现这一目标的一种方法被称为“贪婪的未来。”这是一个归纳证明,你可以证明你的贪婪算法产生的解决方案在第一步中至少是最优的(通过一些措施相当于最后一步的最优性)那么它的第二步也是如此,之后的步骤,依此类推。提示:您可以尝试测量每个人开始编程后事件需要多长时间的最坏情况。在最后一步,当最后一个人开始编程时,这相当于最优性。

另一种证明算法最佳的方法是“交换证明”。这是一种通过矛盾证明的形式,其中您假设某种不同的解是最优的,然后您表明将该解的一部分与一部分交换您的解决方案可以改善所谓的最佳解决方案。这与它曾经是最优的前提相矛盾 - 这证明没有其他解决方案比这更好。所以:假设最佳顺序是不同的,这意味着最后一个人在花费较少时间的其他人之后开始完成。如果你改变那两个人的位置会怎么样?

贪婪的解决方案并不总是最好的,所以如果不是这样,你会想要看看其他技术,比如早期对称破坏和修剪搜索树。