算法课外思考题1116(任务调度)


💡Author:信安2002钱泽枢(ShiQuLiZhi)

🚀欢迎访问我的作业合集:https://sxhthreo.github.io/categories/%E4%BD%9C%E4%B8%9A/

一、题目描述

任务集合 $S$,$\forall i \in S$,$d_i$ 为截止时间,$t_i$ 为加工时间,其中 $d_i,t_i$ 为正整数. 一个调度$f:S\rightarrow N$,$f(i)$ 为任务 $i$ 的开始时间。试设计贪心算法求最大延迟达到最小的调度,即求 $f$ 使得

$\min {f}\left{\max {i \in S}\left{f(i)+t{i}-d{i}\right}\right}$

$\forall i, j \in S, i \neq j, f(i)+t_{i} \leq f(j) \text { or } f(j)+t_{j} \leq f(i)$

并证明你的算法的正确性。

二、算法思路

根据题意,我们可以考虑三种贪心策略,然后我们将通过直观感受排除两种策略,并证明其余一种策略的正确性:

  1. 根据 $t_i$ 从小到大安排
  2. 根据 $d_i-t_i$ 从小到大安排
  3. 根据 $d_i$ 从小到大安排

我们先看策略1,考虑以下场景:

若某个任务的 $t_i$ 很长,且截止时间 $d_i$ 很短,而其他任务 $t_i$ 很短,但 $d_i$ 很长,此时采用策略1,将对 $t_i$ 很长,且截止时间 $d_i$ 很短的任务造成长延迟,故策略1排除;

我们再看策略2,考虑以下场景:

若某个任务的 $d_i$ 很长,且 $t_i$ 很长,但 $d_i-t_i$ 很小,将选择其作为优先调度,而其他任务将造成大量延时, 故策略2排除;

我们考虑策略3的正确性:

定义以下场景为一个逆序:对任务 $i,j$,任务 $i$ 优先于任务 $j$ 调度,但 $d_i>d_j$。

假设存在一个相邻的逆序(任务 $i,j$),交换这两个逆序的任务对其他任务没有影响。

任务ij

交换前任务 $i$ 的开始时间为 $s$,则在交换前,$j$ 的任务延迟大于 $i$,延迟时间为 $t_{max1}=max{s+t_i+t_j-d_j,s+t_i-d_i}=s+t_i+t_j-d_j$

交换后,延迟时间的最大值为 $t_{max2}=max{s+t_j+t_i-d_i,s+t_j-d_j}$

由$d_i>d_j$,则$t_{max1}\geq t_{max2}$,则交换后最大延迟减小。

不断交换逆序对,可以使得整个任务的最大延迟不变或减小,当没有逆序对时,即是最优解。

故贪心算法对于本题是正确的。

三、算法设计

struct management
{
	int id;
	int t;		// 加工时间
	int d;		// 截止时间
	bool operator< (const management& W)const
	{
		return d < W.d;
	}
}m[N];

int manage(int n)
{
	int maxtime = 0, start = 0;		 // maxtime是最大延迟时间
	sort(m, m + n);					// 按照加工时间从小到大排序
	cout << "调度顺序是:";
	for (int i = 0; i < n; i++)
	{
		cout << m[i].id + 1 << " ";
		maxtime = max(maxtime, start + m[i].t - m[i].d);
		start += m[i].t;
	}

	return maxtime;
}

四、运行测试

image-20221120104847856

多次测试,算法运行结果均正确,符合题意。

五、总结与讨论

该算法中需要按照截止时间 $d_i$ 为任务进行排序,需要花费的时间复杂度为 $O(nlog;n)$,遍历一遍所有的任务所需的时间复杂度为 $O(n)$,故该算法的时间复杂度为 $O(nlog;n)$。

贪心算法中,选择一种合适的贪心策略是十分必要的,我们可以通过举出特例的方法排除某些策略,并对我们选出的策略进行正确性证明,通常通过数学归纳法、反证法、迭代策略(不断将可行解换成贪心解达到最优)等方法,最终设计出较为简洁且正确的算法。

附:源代码

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1010;
struct management
{
	int id;
	int t;		// 截止时间
	int d;		// 加工时间
	bool operator< (const management& W)const
	{
		return d < W.d;
	}
}m[N];

int manage(int n)
{
	int maxtime = 0, start = 0;
	sort(m, m + n);		// 按照加工时间从小到大排序
	cout << "调度顺序是:";
	for (int i = 0; i < n; i++)
	{
		cout << m[i].id + 1 << " ";
		maxtime = max(maxtime, start + m[i].t - m[i].d);
		start += m[i].t;
	}

	return maxtime;
}

int main()
{
	int n;
	cin >> n;
	for (int i = 0; i < n; i++)
	{
		cout << "第" << i + 1 << "个调度的加工时间和截止时间是:";
		m[i].id = i;
		cin >> m[i].t;
		cin >> m[i].d;
	}
	
	cout << "最大延迟是" << manage(n) << endl;

	return 0;
}

文章作者: ShiQuLiZhi
版权声明: 本博客所有文章除特别声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 ShiQuLiZhi !
评论
  目录