【OD机试题笔记】排队游戏_刺头学生

题目描述

新来的老师给班里的同学排一个队。

每个学生有一个影力值。

一些学生是刺头,不会听老师的话,自己选位置,非刺头同学在剩下的位置按照能力值从小到大排。

对于非刺头同学,如果发现他前面有能力值比自己高的同学,他不满程度就增加,增加的数量等于前面能力值比他大的同学的个数。

刺头不会产生不满。

如果整个班级累计的不满程度超过k,那么老师就没有办法教这个班级了。

输入描述
输入有三行:

第一行为n,m,k,空格隔开,分别表示班级总人数,刺头人数,最大不满程度k。

第二行为刺头所在位置(从0开始,即排队数组的下标,比如1代表队伍中第2个同学是刺头),位置的数组也是排序的。

第三行有n个数,空格隔开,表示老师排好的队中每个人的能力值,其中非刺头同学一定按照能力值从小到大排好序的。

输出描述
0 表示老师可以继续教这个班级

1 表示老师无法继续教这个班级

备注

  • n 范围是[1,100000]
  • m 范围是 [1,n]
  • k 范国是[1,1000000000]
  • 每位同学的能力值范围是[1000,100000]

输入
4 2 3
0 1
1810 1809 1801 1802

输出
1

说明>
刺头在0,1位置,2号同学不满程度2(前面两个刺头能力值都比他大),3号同学不满程度2,总不满程度4,大于3。输出不能教这班(1)。

输入
4 2 4
0 1
1810 1809 1801 1802

输出
0

说明>
同前,4不大于4,输出能教这个班 (0)

思路

  1. 问题简化:非刺头按能力升序排列,不满仅来自「前面能力更大的刺头」,只需统计这类刺头数量并累加;
  2. 有序前缀数组:动态维护升序的「当前位置前所有刺头能力值」数组;
  3. 二分查找
    • 刺头:二分找插入位置,保持数组升序;
    • 非刺头:二分找第一个大于当前能力值的位置,不满度=数组长度-该位置;
  4. 结果判断:累计不满度>k输出1,否则输出0,过程中不满度超k可提前终止。
  • 时间复杂度:O(n log m)(n为总人数,m为刺头数);
    • 遍历n个同学:O(n);
    • 刺头插入/非刺头查询的二分操作:O(log m);
  • 空间复杂度:空间:O(m)(存储刺头相关数据)。

代码

function binSearch(arr, x) {
   let l = 0, r = arr.length;
   while (l < r) {
      const m = (l + r) >>> 1;
      if (x < arr[m]) {
         r = m;
      } else {
         l = m + 1;
      }
   }
   return l;
}

function solution() {
  const [n, m, k] = readline().split(' ').map(Number);
  const badIds = readline().split(' ').map(Number);
  const abilities = readline().split(' ').map(Number);

  let total = 0;
  // 动态维护一个有序的刺头能力值数组,每次遍历到非刺头,此数组存放的刺头一定是他前面的刺头
  const sortedBadAbilities = [];
  for (let i = 0; i < n; i++) {
     const curAbility = abilities[i];
     if (badIds.includes(i)) {
        const pos = binSearch(sortedBadAbilities, curAbility);
        sortedBadAbilities.splice(pos, 0, curAbility);
     } else {
        const pos = binSearch(sortedBadAbilities, curAbility);
        // 这个位置之后的所有元素都比 curAbility 大
        const dissatisfaction = sortedBadAbilities.length - pos;
    	total += dissatisfaction;
    	
    	// 提前退出
    	if (total > k) {
    	    break;
    	}
     }
  }
  
  console.log(total > k ? 1 : 0);
}


// 测试用例
const cases = [
`4 2 3
0 1
1810 1809 1801 1802`,
`4 2 4
0 1
1810 1809 1801 1802`   
];

let caseIndex = 0, lineIndex = 0;
const readline = (() => {
    let lines = [];
    return () => {
        if (!lineIndex) lines = cases[caseIndex].trim().split('\n').map(l => l.trim());
        return lines[lineIndex++];
    };
})();

cases.forEach((_, i) => {
    caseIndex = i;
    lineIndex = 0;
    solution();
    console.log('-------');
});

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值