最佳调度问题

来源:互联网 发布:李小冉 知乎 编辑:程序博客网 时间:2024/05/16 11:33

【问题描述】
假设有n个任务由k个可并行工作的机器完成。完成任务i需要的时间为ti。试设计一个算法找出完成这n个任务的最佳调度,使得完成全部任务的时间最早。
【编程任务】
对任意给定的整数n和k,以及完成任务i需要的时间为ti,i=1~n。编程计算完成这n个任务的最佳调度。
【输入格式】
第一行有2 个正整数n和k。第2 行的n个正整数是完成n个任务需要的时间。
【输出格式】
将计算出的完成全部任务的最早时间输出。
【输入样例】
7 3
2 14 4 16 6 5 3
【输出样例】
17


这道题一开始没什么思路,贪心法不能得到最优解, 以为要把每个机器里的任务枚举出来。
其实可以这样想,每个机器里的任务数不确定,但每个任务可以放的机器数是确定的,所以可以搜索每个任务放到哪个机器里。

//machine-Milky#include<cstdio>#include<cstring>#include<algorithm>using namespace std;int n,k,a[1005],c[1005],minnow=0x7fffffff;void dfs(int cur,int maxnow){    if(cur>n) {minnow=maxnow; return;}    for(int i=1;i<=k;++i)    {        if(c[i]+a[cur]>=minnow) continue;        c[i]+=a[cur];        dfs(cur+1,max(maxnow,c[i]));        c[i]-=a[cur];    }}int main(){    freopen("machine.in","r",stdin);    freopen("machine.out","w",stdout);    scanf("%d%d\n",&n,&k);    for(int i=1;i<=n;++i) scanf("%d",&a[i]);    dfs(1,0);    printf("%d\n",minnow);    return 0;    fclose(stdin);    fclose(stdout);}

交上去有4个点TLE,loli说是因为minnow的初始值太大,限制不严格。
虽然贪心得出的解不是最优的,但也算是比较小的解,所以可以先贪心跑出一个minnow作搜索的限制。

//machine-Milky#include<cstdio>#include<cstring>#include<algorithm>using namespace std;int n,k,a[1005],c[1005],minnow;void swapp(int &x,int &y){    x^=y; y^=x; x^=y;}int greedy(){    int g[1005];    for(int i=1;i<=n;++i)    {        g[k]+=a[i];        for(int j=k-1,t=k;j>=1;--j)        {            if(g[j]<g[t])            {                swapp(g[j],g[t]); t=j;            }            else break;        }    }    return g[1];}void dfs(int cur,int maxnow){    if(cur>n) {minnow=maxnow; return;}    for(int i=1;i<=k;++i)    {        if(c[i]+a[cur]>=minnow) continue;        c[i]+=a[cur];        dfs(cur+1,max(maxnow,c[i]));        c[i]-=a[cur];    }}bool cmp(int x,int y) {return x>y;}int main(){    freopen("machine.in","r",stdin);    freopen("machine.out","w",stdout);    scanf("%d%d\n",&n,&k);    for(int i=1;i<=n;++i) scanf("%d",&a[i]);    sort(a+1,a+n+1,cmp);    minnow=greedy();    dfs(1,0);    printf("%d\n",minnow);    return 0;    fclose(stdin);    fclose(stdout);}

虽然比刚才快了,但还是有两个点TLE。

loli的标准程序是把minnow初始成 sum/k+a[n] 即平均值加上一个最小值。但我举出一个反例,当n=k=2,输入 999 1 的时候就会出错。因为某个元素非常大,大于了平均值于最小值的和,就会直接输出minnow。

于是AH多次在loli的程序上加条件,在我多次举出反例推翻之后,终于得出一个似乎正确的方法。
(手动忽略此处)如果最大值大于平均值,minnow为最大值加最小值,否则为平均值加最小值。

但我又找出一个反例。然后我觉得他完全是在没有证明的情况下鬼扯。

当最大值大于平均值,minnow=最大值加最小值 的方法是对的,下面给出证明:我们假设取出最大的和最小的放到第一个容器里,剩下的物品分到 k-1 个容器里 它们的最大值永远不会超过第一个容器,因为 sum < n*maxn,所以 sum-maxn < (n-1) * maxn 实际上直接输出最大值就可以!

至于平均值大于最大值的情况。。我们继续探讨。

最后,有个正确的方法,跟第一个程序一样,minnow还是无穷大,只不过在搜索前加一个排序。
因为排序之后,能够更快地得到最优解,在此之后的搜索都会被剪枝,大大提升效率。此处代码省略。