【2016 泉市教科】任务调度

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

任务调度

问题描述

  “选课加社一时爽,时间规划火葬场”话说小 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.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值