(B卷,200分)- 排队游戏(Java & JS & Python & C)

题目描述

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

每个学生有一个影力值。

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

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

刺头不会产生不满。

如果整个班级累计的不满程度超过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)

题目解析

本题可以使用二分查找解题。

题目说:

非刺头同学在剩下的位置按照能力值从小到大排。

因此,每个非刺头学生,其实只需要关注他前面的刺头学生即可,因为他前面的非刺头学生的能力值一定不大于他。

我们可以定义一个集合 pricks 来记录刺头学生的能力值。

因此,我们遍历所有学生能力值时:

  • 如果当前位置是刺头学生(根据第二行输入的刺头学生位置判断),那么就将当前学生能力值ability加入到pricks中,这里我们需要保证有序插入到 pricks 中。
  • 如果当前位置是非刺头学生,那么我们需要找到当前学生能力值ability在pricks中的排名rank(其实也是有序插入位置),找到排名后,范围[rank, pricks.length – 1]内刺头学生都是能力比当前学生高的

按此逻辑,我们只要收集非刺头学生 前面 能力比自己高的 非刺头学生个数,如果最后总个数 > k,则老师无法教此班级,否则就可以。

上面逻辑中,我们有两个动作:

  • 向 pricks 中有序插入 ability(注意,本题pricks从空开始有序插入,因此可以保证一直有序)
  • 在 pricks 中找到 ability 的排名 (最后一个有序插入位置)

而这可以基于二分查找实现。

但是,pricks 中是有可能存在多个相同能力值的,对于这种情况,我们应该将ability插入到pricks中和ability相同的最后一个能力值的后面,这样才能保证 ability 被插入后,其后面值都比 ability 高。

关于在pricks中寻找和ability相同的最后一个能力值的位置,具体实现可以参考下面博客中searchLast方法:

JS算法源码

const rl = require("readline").createInterface({ input: process.stdin });
var iter = rl[Symbol.asyncIterator]();
const readline = async () => (await iter.next()).value;

void (async function () {
  // 班级总人数, 刺头人数, 最大不满程度
  const [n, m, k] = (await readline()).split(" ").map(Number);
  // 刺头所在位置
  const pricks_idx = new Set((await readline()).split(" ").map(Number));
  // 所有学生的能力值
  const all = (await readline()).split(" ").map(Number);

  // 总不满意程度
  let unHappy = 0;

  // 记录所有刺头的能力
  const pricks = [];
  for (let i = 0; i < n; i++) {
    // 当前学生能力
    const ablity = all[i];

    // 当前学生能力在刺头中的排名, 这里使用二分查找pricks中最后一个等于ability的位置,如果pricks中没有等于ability的值,则返回第一个大于ability的位置
    let rank = searchLast(pricks, ablity);

    if (rank < 0) {
      rank = -rank - 1;
    } else {
      rank += 1;
    }

    if (pricks_idx.has(i)) {
      // 如果当前学生是刺头,那么就(有序)插入到刺头学生中
      pricks.splice(rank, 0, ablity);
    } else {
      // 如果当前学生是非刺头,那么 [rank ~ pricks.size() - 1] 范围的所有刺头学生都是能力值比当前学生高的
      unHappy += pricks.length - rank;
    }
  }

  console.log(unHappy > k ? 1 : 0);
})();

function searchLast(arr, target) {
  let low = 0;
  let high = arr.length - 1;

  while (low <= high) {
    const mid = (low + high) >> 1;
    const midVal = arr[mid];

    if (midVal > target) {
      high = mid - 1;
    } else if (midVal < target) {
      low = mid + 1;
    } else {
      // 向右延伸判断,mid是否为target数域的右边界,即最后一次出现的位置
      if (mid == arr.length - 1 || arr[mid] != arr[mid + 1]) {
        return mid;
      } else {
        low = mid + 1;
      }
    }
  }

  return -low - 1; // 找不到则返回插入位置
}

Java算法源码

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Scanner;

public class Main {

  public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);

    // 班级总人数
    int n = sc.nextInt();
    // 刺头人数
    int m = sc.nextInt();
    // 最大不满程度
    int k = sc.nextInt();

    // 刺头所在位置
    HashSet<Integer> pricks_idx = new HashSet<>();
    for (int i = 0; i < m; i++) {
      pricks_idx.add(sc.nextInt());
    }

    // 总不满意程度
    int unHappy = 0;

    // 记录所有刺头的能力
    ArrayList<Integer> pricks = new ArrayList<>();
    for (int i = 0; i < n; i++) {
      // 当前学生能力
      int ability = sc.nextInt();

      // 当前学生能力在刺头中的排名, 这里使用二分查找pricks中最后一个等于ability的位置,如果pricks中没有等于ability的值,则返回第一个大于ability的位置
      int rank = searchLast(pricks, ability);

      if (rank < 0) {
        rank = -rank - 1;
      } else {
        rank += 1;
      }

      if (pricks_idx.contains(i)) {
        // 如果当前学生是刺头,那么就(有序)插入到刺头学生中
        pricks.add(rank, ability);
      } else {
        // 如果当前学生是非刺头,那么 [rank ~ pricks.size() - 1] 范围的所有刺头学生都是能力值比当前学生高的
        unHappy += pricks.size() - rank;
      }
    }

    System.out.println(unHappy > k ? 1 : 0);
  }

  public static int searchLast(ArrayList<Integer> list, int target) {
    int low = 0;
    int high = list.size() - 1;

    while (low <= high) {
      int mid = (low + high) >> 1;
      int midVal = list.get(mid);

      if (midVal > target) {
        high = mid - 1;
      } else if (midVal < target) {
        low = mid + 1;
      } else {
        // 向右延伸判断,mid是否为target数域的右边界,即最后一次出现的位置
        if (mid == list.size() - 1 || list.get(mid) - list.get(mid + 1) != 0) {
          return mid;
        } else {
          low = mid + 1;
        }
      }
    }

    return -low - 1; // 找不到则返回插入位置
  }
}

Python算法源码

n, m, k = map(int, input().split())  # 班级总人数, 刺头人数, 最大不满程度
pricks_idx = set(map(int, input().split()))  # 刺头所在位置
all_ability = list(map(int, input().split()))  # 所有学生能力


def searchLast(arr, target):
    low = 0
    high = len(arr) - 1

    while low <= high:
        mid = (low + high) >> 1
        midVal = arr[mid]

        if midVal > target:
            high = mid - 1
        elif midVal < target:
            low = mid + 1
        else:
            if mid == len(arr) - 1 or arr[mid] != arr[mid + 1]:
                return mid
            else:
                low = mid + 1

    return -low - 1


def getResult():
    # 总不满意程度
    unHappy = 0

    # 记录所有刺头的能力
    pricks = []
    for i in range(n):
        # 当前学生能力
        ability = all_ability[i]

        # 当前学生能力在刺头中的排名, 这里使用二分查找pricks中最后一个等于ability的位置,如果pricks中没有等于ability的值,则返回第一个大于ability的位置
        rank = searchLast(pricks, ability)

        if rank < 0:
            rank = -rank - 1
        else:
            rank += 1

        if i in pricks_idx:
            # 如果当前学生是刺头,那么就(有序)插入到刺头学生中
            pricks.insert(rank, ability)
        else:
            # 如果当前学生是非刺头,那么 [rank ~ pricks.size() - 1] 范围的所有刺头学生都是能力值比当前学生高的
            unHappy += len(pricks) - rank

    return 1 if unHappy > k else 0


print(getResult())

C算法源码

#include <stdio.h>

#define MAX_SIZE 100000

typedef int bool;
#define TRUE 1
#define FALSE 0

int searchLast(int* arr, int arr_size, int target);
void insert(int* arr, int* arr_size, int insert_idx, int insert_val);

int main()
{
// 班级总人数, 刺头人数, 最大不满程度
int n, m, k;
scanf("%d %d %d", &n, &m, &k);

// 刺头所在位置
bool isPrick[MAX_SIZE] = {FALSE};
for(int i=0; i<m; i++) {
int idx;
scanf("%d", &idx);
isPrick[idx] = TRUE;
}

// 总不满意程度
int unHappy = 0;

// 记录所有刺头的能力
int pricks[MAX_SIZE];
int pricks_size = 0;

for(int i=0; i<n; i++) {
// 当前学生能力
int ability;
scanf("%d", &ability);

// 当前学生能力在刺头中的排名, 这里使用二分查找pricks中最后一个等于ability的位置,如果pricks中没有等于ability的值,则返回第一个大于ability的位置
int rank = searchLast(pricks, pricks_size, ability);

if(rank < 0) {
rank = -rank - 1;
} else {
rank += 1;
}

if(isPrick[i]) {
// 如果当前学生是刺头,那么就(有序)插入到刺头学生中
insert(pricks, &pricks_size, rank, ability);
} else {
// 如果当前学生是非刺头,那么 [rank ~ pricks.size() - 1] 范围的所有刺头学生都是能力值比当前学生高的
unHappy += pricks_size - rank;
}
}

printf("%dn", unHappy > k ? 1 : 0);

return 0;
}

int searchLast(int* arr, int arr_size, int target)
{
int low = 0;
int high = arr_size- 1;

while(low <= high) {
int mid = (low + high) >> 1;
int midVal = arr[mid];

if(midVal > target)
{
high = mid - 1;
} 
else if(midVal < target)
{
low = mid + 1;
}
else
{
// 向右延伸判断,mid是否为target数域的右边界,即最后一次出现的位置
if(mid == arr_size - 1 || arr[mid] - arr[mid+1] != 0)
{
return mid;
}
else
{
low = mid + 1;
}
}
}

return -low - 1; // 找不到则返回插入位置
}

void insert(int* arr, int* arr_size, int insert_idx, int insert_val)
{
for(int i = *arr_size - 1; i >= insert_idx; i--) {
arr[i+1] = arr[i];
}

arr[insert_idx] = insert_val;

(*arr_size)++;
}

免责声明:

1、IT资源小站为非营利性网站,全站所有资料仅供网友个人学习使用,禁止商用
2、本站所有文档、视频、书籍等资料均由网友分享,本站只负责收集不承担任何技术及版权问题
3、如本帖侵犯到任何版权问题,请立即告知本站,本站将及时予与删除下载链接并致以最深的歉意
4、本帖部分内容转载自其它媒体,但并不代表本站赞同其观点和对其真实性负责
5、一经注册为本站会员,一律视为同意网站规定,本站管理员及版主有权禁止违规用户
6、其他单位或个人使用、转载或引用本文时必须同时征得该帖子作者和IT资源小站的同意
7、IT资源小站管理员和版主有权不事先通知发贴者而删除本文

0

评论0

站点公告

没有账号?注册  忘记密码?