任务调度
问题描述
“选课加社一时爽,时间规划火葬场”话说小 A 初来乍到,在大学里选上了满满的课,还加入了很多很多的社团。刚刚过不到几个月就难以承受,发现自己如果三心二意,做一会儿微积分作业,写一会儿社团活动计划,最后便会什么事情都做不成。他决定有所取舍,但他又不愿意舍弃太多,这一重要的抉择就交给聪明的你了!
输入
输入文件名为 schedule.in。
第一行两个正整数 n,K。n 表示任务列表的长度,K 表示退出的类型总数上限。
接下来 N 行,每行仅一个正整数 A i 。为 i 号工作由编码 A i 的课程或社团派发。
输出
输出文件名为 schedule.out。
仅一个正整数,为最长的最长完美学生序列。
Sample Input
9 1
2
7
3
7
7
3
7
5
7
Sample Output
4
样例说明
总共有 9 项工作,最多只能退出一门课程或一个社团。
若不退出,拥有同样编码的工作的长度为 2。
若退出 3 号项目,则任务序列变成 2 7 7 7 7 5 7,拥有同样编码的工作的长度为 4。
数据范围
对于 10%的数据,n≤10。
对于 30%的数据,n≤10^3 。
对于 100%的数据,1≤n≤10^5 。
题解
对于30%的数据点,可以有多种解法,时间复杂度一般处于O(n^2)、O(n^2lgon)、O(n^3)等等,以下附上一个Pascal的二分答案,时间复杂度应该是O(n^2) (没去算)。
program schedule;
var a:array[0..100001] of longint;
i,j,n,l,r,mid,k:longint;
procedure putin;
begin
assign(input,'schedule.in');reset(input);
assign(output,'schedule.out');rewrite(output);
end;
procedure outit;
begin
close(input);
close(output);
end;
function check(g:longint):boolean;
var i,j,cnt,tmp,now,max,ans,tot:longint;
f:array[0..100000] of boolean;
begin
max:=-maxlongint;
for i:=1 to n do
begin
now:=i+1;
cnt:=0;
tmp:=1;
tot:=0;
fillchar(f,sizeof(f),false);
while (cnt<=k) and (now <= n+1) do
begin
if a[now] = a[i] then
begin inc(tmp);inc(now);end
else
begin
if f[a[now]] then begin inc(now) end
else
begin
if cnt = k then
begin
if tmp>max then max:=tmp;
if tmp >= g then exit(true);
break;
end
else
begin
if tmp>max then max:=tmp;
if tmp>g then exit(true);
f[a[now]] :=true;
inc(cnt);
inc(now);
end;
end;
end;
end;
end;
if max<g then exit(false);
end;
begin
putin;
read(n,k);
for i:=1 to n do read(a[i]);
l:=1;
r:=n;
a[n+1]:=91354;
while l<r do
begin
mid:=(l+r) div 2 +1;
if check(mid) then
l:=mid
else
r:=mid-1;
end;
writeln(l);
outit;
end.
对于100%的数据点,这里可以发现,如果在某一段区间里面操作得到最终答案,那该段区间的种类数一定会小于等于k+1。我们可以把这n个数转成一个队列,每次读入都对该队列进行操作。初始head、tail均为1,一旦队列内的种数超过了k,开始出队,每次出队时将队列中的众数与ans比较,取最大值。
由于这道题的数据有点大,在计算数的种类时,如果单纯采用a[i]记录数值为i的数出现的个数,那只有爆内存/RE这些结果。而应对这种情况,可以采用哈希/离散化来解决。Pascal用户的话,还是建议使用离散话解决(谁叫他没有哈希表?)。
附上Pascal代码:
program schedule;
var queue,a:array[0..100001] of longint;//a[i]记录数值为i的数出现的次数;
b,c,d:array[0..1000001] of longint;//b[]、c[]、d[]用来离散化数据。
i,n,j,k,m,ans,cnt,head,tail:longint;
function max(a,b:longint):longint;
begin
if a>b then exit(a)
else exit(b);
end;
procedure sort(l,r: longint);//离散化中的排序。
var
i,j,x,y: longint;
begin
i:=l;
j:=r;
x:=b[(l+r) div 2];
repeat
while b[i]<x do
inc(i);
while x<b[j] do
dec(j);
if not(i>j) then
begin
y:=b[i];
b[i]:=b[j];
b[j]:=y;
y:=c[i];
c[i]:=c[j];
c[j]:=y;
inc(i);
j:=j-1;
end;
until i>j;
if l<j then
sort(l,j);
if i<r then
sort(i,r);
end;
procedure putin;
begin
assign(input,'schedule.in');reset(input);
assign(output,'schedule.out');rewrite(output);
end;
procedure outit;
begin
close(input);
close(output);
end;
begin
putin;
read(n,k);
head:=1;
for i:=1 to n do read(b[i]);//b[i]记录读入的数值
for i:=1 to n do c[i]:=i; //离散化开始
sort(1,n);
d[c[0]]:=0;
for i:=1 to n do
if (b[i]=b[i-1]) then
d[c[i]]:=d[c[i-1]]
else
d[c[i]]:=i;
for i:=1 to n do
b[i]:=d[i]; //离散化结束
for i:=1 to n do
begin
j:=b[i];
queue[i]:=j;
inc(a[queue[i]]);
inc(tail);
if a[queue[i]] = 1 then inc(cnt); //cnt计算队列中的数的种类个数
if cnt>k+1 then
begin
while cnt>k+1 do //出队过程
begin
ans:=max(ans,a[queue[head]]);//每次将出队的点的个数与ans比较
dec(a[queue[head]]) ;
if a[queue[head]] = 0 then dec(cnt); //循环直到cnt<=k+1
inc(head);
end;
end;
ans:=max(ans,a[queue[head]]);
end;
for i:=head to tail do
ans:=max(ans,a[queue[i]]); //循环确定是否有cnt<=k+1 但数值比ans大的点
writeln(ans);
outit;
end.

本文介绍了一个任务调度问题,通过合理安排不同课程和社团的任务,以达到最长完美学生序列的目标。提供了两种算法实现方案,包括时间复杂度为O(n^2)的二分查找法和适用于大数据量的队列操作及离散化处理方法。
307

被折叠的 条评论
为什么被折叠?



