• 两类带时限的作业排序问题


    1   带时限的作业排序问题

    问题描述:

    设有一个单机系统、无其它资源限制且每个作业运行相等时间,不妨假定每个作业运行 1 个单位时间。现有 n 个作业,每个作业都有一个截止期限di>0,di 为整数。如果作业能够在截止期限之内完成,可获得 pi>0 的收益。问题要求得到一种作业调度方案,该方案给出作业的一个子集和该作业子集的一种排列,使得若按照这种排列次序调度作业运行,该子集中的每个作业都能如期完成,并且能够获得最大收益。

    输入:

    第一行输入 n 的值,以下 n 行输入作业号 i,收益 pi,截止期限 di。

    输出:

    n 个作业的一个最优子集。

    示例 

    输入:

          4

          1 100 2

          2 10 1

          3 15 2

          4 27 1

          1 4

    输出: 0 0 1 1

     

    2   带时限的作业排序问题II

    问题描述:

    带时限的作业排序问题可描述为:设有 n 个作业和一台处理机,每个作业所需的处理时间、要求的时限和收益可用三元组(pi, di, ti),0<=i<n 表示,其中,作业 i 的所需时间为 ti,如果作业 i 能够在时限 di 内完成,将可收益 pi,求使得总收益最大的作业子集 J。

    输入:

    第一行输入 n 的值;第二行输入 pi;第三行输入 di;第四行输入 ti (i=0,…,n-1 且作业已经按时限非减次序排列)。

    输出:

    xi(用固定长度 n-元组 xi 表示,xi=0 或 1,i=0,…,n-1)。

    示例 

    输入:

          4

          5 3 6 10

          1 1 2 3

          1 1 1 2

    输出: 0 0 1 1

     

    第1题

    思路分析

    这题的思路比较明显,就是每次都选取作业收益最大的,并把它放在允许的最大的截止时间内完成,符合贪心算法的基本思想。将输入的每个作业按收益从大到小进行排序,用一个数组vis表示某时刻是否已经有作业正在运行,vis[i]=1,表示时间i被占用 。然后从第1个作业开始往后搜索,将该作业安排到vis[d]时间运行,如果d已经有安排,将从d-1开始往前搜索,直到找到一个未被占用的时间点。如果找不到空时间点,则跳过此作业,继续向后搜索直到所有作业都搜索完。

     1 #include <iostream>
     2 #include <stdio.h>
     3 #include <algorithm>
     4 #include <string.h>
     5 
     6 using namespace std;
     7 
     8 #define MAXN 1100
     9 
    10 struct Node
    11 {
    12     int id; //编号 
    13     int p;  //收益 
    14     int d;  //截止期限 
    15 }node[MAXN]; 
    16 
    17 int n;
    18 int vis[MAXN];      //第i个单位时间是否有作业在运行 
    19 vector<int> res;    //保存结果 
    20 
    21 bool cmp(Node a, Node b)
    22 {
    23     return a.p > b.p;
    24 }
    25 
    26 int main()
    27 {
    28     scanf("%d", &n);
    29     for(int i = 0; i < n; ++i)
    30         scanf("%d%d%d", &node[i].id, &node[i].p, &node[i].d);
    31     //按照收益从大到小排序 
    32     sort(node, node+n, cmp);
    33     memset(vis, 0, sizeof(vis));
    34     
    35     for(int i = 0; i < n; ++i)
    36     {
    37         // 如果到期时间没有被占用,则直接占用到期时间;
    38         // 否则,继续往前找一个未被占用的时间 
    39         if(!vis[node[i].d])
    40         {
    41             vis[node[i].d] = 1;
    42             res.push_back(node[i].id); 
    43         }
    44         else
    45         {
    46             for(int j = node[i].d-1; j >= 1; --j)
    47             {
    48                 if(!vis[j])
    49                 {
    50                     res.push_back(node[i].id); 
    51                     vis[j] = 1;
    52                     break;
    53                 }
    54             }
    55         }
    56     } 
    57     
    58     for(int i = 0; i < res.size(); ++i)
    59         printf("%d ", res[i]);
    60 
    61     return 0;
    62 }

    复杂度分析

    从代码来看,sort函数排序是O(nlogn)的时间复杂度,最差情况下,d >= n,且每组数据的截止期限都是d时,每次都要往前找一个空的时间点,此时需要找1+2+3+…+n次,所以耗费的时间为O(n2)。由于各个子块不是嵌套的而是顺序的,所以时间复杂度取最高的那个,所以整体来说时间复杂度是O(n2)。

    对于空间复杂度,vis[]数组的大小由最大截止日期d决定。所以空间复杂度是O(d)。

    第2题

    这题和前面这题很相似,但其实解法有很大的差异。这题如果还是按前面那题的贪心思想去解题,是做不出来的,很难找到贪心算法中的最优度量标准。这题是一道比较难解决的问题,我在网上也没找到解决方法。后来询问一些算法比较厉害的同学之后,发现这题其实就是个0-1背包问题。

    解题策略就是先选出最大的截止时间dmax,然后从1到dmax,进行0-1背包算法的操作。通过0-1背包的思路就可以找到最优解,这题相较于经典的0-1背包问题还是多了一些内容,那就是要记录背包的路径,最后输出作业的子集的过程其实就是打印背包路径的过程。

     1 #include <iostream>
     2 #include <stdio.h>
     3 
     4 using namespace std;
     5 
     6 int n;
     7 int p[110], d[110], t[110];
     8 int memo[1110];          //memo[i]表示截止期限为i时的最大收益 
     9 int path[1110][1110];   //记录背包路径 
    10 
    11 //递归打印背包路径 
    12 void printPath(int i, int j)
    13 {
    14     if(i < 0 || j < 0)
    15         return;
    16     
    17     if(path[i][j] == 1)
    18         printPath(i-1, j-t[i]); 
    19     else
    20         printPath(i-1, j);
    21     
    22     printf("%d ", path[i][j]);
    23 
    24 }
    25 
    26 int main()
    27 {
    28     scanf("%d", &n);
    29     int dmax = -1;  //最大截止时间 
    30     for(int i = 0; i < n; ++i)
    31         scanf("%d", &p[i]);
    32     for(int i = 0; i < n; ++i)
    33         scanf("%d", &d[i]);
    34     
    35     //题中已经说明输入按时限非减次序排列,所以d[n-1]就是最大的截止期限 
    36     dmax = d[n-1];  
    37     
    38     for(int i = 0; i < n; ++i)
    39         scanf("%d", &t[i]);
    40         
    41     for(int i = 0; i <= dmax; ++i)
    42     {
    43         if(i >= t[0])
    44         {
    45             memo[i] = p[0]; 
    46             path[0][i] = 1;   //记录路径
    47         }
    48         else
    49             memo[i] = 0;
    50     } 
    51     
    52     for(int i = 1; i < n; ++i)
    53     {
    54         for(int j = dmax; j >= t[i]; --j)
    55         {
    56             if(memo[j] < p[i] + memo[j-t[i]])
    57             {   //物品装入背包,记录路径
    58                 memo[j] = p[i] + memo[j-t[i]];
    59                 path[i][j] = 1;     //记录路径 
    60             }
    61         }
    62     }
    63 
    64     //递归打印背包路径 
    65     printPath(n-1, dmax);
    66     
    67     return 0;
    68 }

    复杂度分析

    从代码来看,这个程序运行的时间主要消耗在第一次的双重循环中,外层循环的次数为n,内层循环次数为dmax,所以这个算法的平均时间复杂度为O(n*dmax)。

  • 相关阅读:
    php中的多态
    面向对象的继承与组合
    PHP中的__call和__callStatic方法
    PHP中的__set和__get方法
    PHP中对象的本质
    mysql字符串查找(统计客源)
    linux查看文件大小
    mysql常用字符串操作函数大全,以及实例
    mysql滑动订单问题
    mysql列反转Pivoting
  • 原文地址:https://www.cnblogs.com/FengZeng666/p/13929488.html
Copyright © 2020-2023  润新知