(B卷,200分)- 购物(Java & JS & Python)

题目描述

商店里有N件唯一性商品,每件商品有一个价格,第 i 件商品的价格是 ai。

一个购买方案可以是从N件商品种选择任意件进行购买(至少一件),花费即价格之和。

现在你需要求出所有购买方案中花费前K小的方案,输出这些方案的花费。

当两个方案选择的商品集合至少有一件不同,视为不同方案,因此可能存在两个方案花费相同。

输入描述

输入数据含两行:

  • 第一行包含两个整数N,K,整数之间通过空格隔开。分别表示商品的个数,以及需要求得的花费个数。1 ≤ N ≤ 10000,1 ≤ K ≤ min(2^N – 1,100000)
  • 第二行包含N个整数a1,a2,…,an,整数之间通过空格隔开。表示N件商品的价格。1 ≤ a1 ≤ a2 ≤ … ≤ an ≤ 10000

输出描述

按花费从小到大的顺序依次输出K行,一行一个整数。表示花费前K小的购买方案的花费。

用例

输入 5 6
1 1 2 3 3
输出 1
1
2
2
3
3
说明

输入 3 5
1 100 101
输出 1
100
101
101
102
说明

题目解析

本题其实就是要我们求解:全组合中前k小组合(按照组合之和大小排序)。

具体解析可以参考:

JS算法源码

/* JavaScript Node ACM模式 控制台输入获取 */
const readline = require("readline");

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

const lines = [];
rl.on("line", (line) => {
  lines.push(line);

  if (lines.length == 2) {
    const [n, k] = lines[0].split(" ").map(Number);
    const nums = lines[1].split(" ").map(Number);
    getResult(n, k, nums);
    lines.length = 0;
  }
});

// 算法入口
function getResult(n, m, nums) {
  nums.sort((a, b) => a - b);

  // 对于一个组合模型,其”将要产生的新组合“之和越小,则优先级越高
  // curSum + nums[nextIdx] 为 ”将要产生的新组合“之和
  const pq = new PriorityQueue(
    (a, b) => a.curSum + nums[a.nextIdx] - (b.curSum + nums[b.nextIdx])
  );

  // 空组合的和为0, 将要加入的新元素是nums[0], 即索引0的元素,其将要产生的新组合之和为 0 + nums[0]
  let c = new CombineModel(0, 0);

  for (let i = 0; i < m; i++) {
    // 打印第 i 小组合
    console.log(c.curSum + nums[c.nextIdx]);

    // c是当前最小组合模型,最小的组合模型指的是将要产生的新组合之和在对应轮次中最小
    // 如果当前组合模型c还有可合入的下一个元素,即c.nextIdx + 1 < n, 则说明可以基于当前组合模型产生一个新组合
    if (c.nextIdx + 1 < n) {
      // 基于当前组合模型产生的新组合,也是本轮最小的组合,即第 i 小组合
      pq.offer(new CombineModel(c.curSum + nums[c.nextIdx], c.nextIdx + 1));

      // 当前组合需要更新nextIdx后,重新加入优先队列
      c.nextIdx += 1;
      pq.offer(c);
    }

    // 取出优先队列中最小组合(注意这里的最小,指的是基于当前组合,将要产生的新组合之和最小)
    c = pq.poll();
  }
}

// 组合模型
class CombineModel {
  constructor(curSum, nextIdx) {
    this.curSum = curSum; // 当前组合之和
    this.nextIdx = nextIdx; // 将要被加入当前组合的新元素索引位置
  }
}

// 基于堆实现优先队列
class PriorityQueue {
  constructor(cpr) {
    this.queue = [];
    this.size = 0;
    this.cpr = cpr;
  }

  swap(i, j) {
    let tmp = this.queue[i];
    this.queue[i] = this.queue[j];
    this.queue[j] = tmp;
  }

  // 上浮
  swim() {
    let ch = this.queue.length - 1;

    while (ch !== 0) {
      let fa = Math.floor((ch - 1) / 2);

      const ch_node = this.queue[ch];
      const fa_node = this.queue[fa];

      if (this.cpr(ch_node, fa_node) < 0) {
        this.swap(ch, fa);
        ch = fa;
      } else {
        break;
      }
    }
  }

  // 下沉
  sink() {
    let fa = 0;

    while (true) {
      let ch_left = 2 * fa + 1;
      let ch_right = 2 * fa + 2;

      let ch_max;
      let ch_max_node;

      const fa_node = this.queue[fa];
      const ch_left_node = this.queue[ch_left];
      const ch_right_node = this.queue[ch_right];

      if (ch_left_node && ch_right_node) {
        // 注意这里应该要>=0,因为左边优先级高
        if (this.cpr(ch_left_node, ch_right_node) <= 0) {
          ch_max = ch_left;
          ch_max_node = ch_left_node;
        } else {
          ch_max = ch_right;
          ch_max_node = ch_right_node;
        }
      } else if (ch_left_node && !ch_right_node) {
        ch_max = ch_left;
        ch_max_node = ch_left_node;
      } else if (!ch_left_node && ch_right_node) {
        ch_max = ch_right;
        ch_max_node = ch_right_node;
      } else {
        break;
      }

      // 注意这里应该要>0,因为父优先级高
      if (this.cpr(ch_max_node, fa_node) < 0) {
        this.swap(ch_max, fa);
        fa = ch_max;
      } else {
        break;
      }
    }
  }

  // 向优先队列中加入元素
  offer(ele) {
    this.queue.push(ele);
    this.size++;
    this.swim();
  }

  // 取出最高优先级元素
  poll() {
    this.swap(0, this.queue.length - 1);
    this.size--;
    const ans = this.queue.pop();
    this.sink();
    return ans;
  }
}

Java算法源码

import java.util.Arrays;
import java.util.PriorityQueue;
import java.util.Scanner;

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

    int n = sc.nextInt();
    int k = sc.nextInt();

    int[] nums = new int[n];
    for (int j = 0; j < n; j++) {
      nums[j] = sc.nextInt();
    }

    getResult(n, k, nums);
  }

  static class CombineModel {
    int curSum; // 当前组合之和
    int nextIdx; // 将要被加入当前组合的新元素索引位置

    public CombineModel(int curSum, int nextIdx) {
      this.curSum = curSum;
      this.nextIdx = nextIdx;
    }
  }

  public static void getResult(int n, int m, int[] nums) {
    Arrays.sort(nums);

    // 对于一个组合模型,其”将要产生的新组合“之和越小,则优先级越高
    // curSum + nums[nextIdx] 为 ”将要产生的新组合“之和
    PriorityQueue<CombineModel> pq =
        new PriorityQueue<>((a, b) -> a.curSum + nums[a.nextIdx] - (b.curSum + nums[b.nextIdx]));

    // 空组合的和为0, 将要加入的新元素是nums[0], 即索引0的元素,其将要产生的新组合之和为 0 + nums[0]
    CombineModel c = new CombineModel(0, 0);

    for (int i = 1; i <= m; i++) {
      // 打印第 i 小组合
      System.out.println(c.curSum + nums[c.nextIdx]);

      // c是当前最小组合模型,最小的组合模型指的是将要产生的新组合之和在对应轮次中最小
      // 如果当前组合模型c还有可合入的下一个元素,即c.nextIdx + 1 < n, 则说明可以基于当前组合模型产生一个新组合
      if (c.nextIdx + 1 < n) {
        // 基于当前组合模型产生的新组合,也是本轮最小的组合,即第 i 小组合
        pq.offer(new CombineModel(c.curSum + nums[c.nextIdx], c.nextIdx + 1));

        // 当前组合需要更新nextIdx后,重新加入优先队列
        c.nextIdx += 1;
        pq.offer(c);
      }

      // 取出优先队列中最小组合(注意这里的最小,指的是基于当前组合,将要产生的新组合之和最小)
      c = pq.poll();
    }
  }
}

Python算法源码

import heapq
import queue

# 输入获取
n, m = map(int, input().split())
nums = list(map(int, input().split()))


# 组合模型
class CombineModel:
    def __init__(self, curSum, nextIdx):
        self.curSum = curSum  # 当前组合之和
        self.nextIdx = nextIdx  # 将要被加入当前组合的新元素索引位置

    def __lt__(self, other):
        # 对于一个组合模型,其”将要产生的新组合“之和越小,则优先级越高
        # curSum + nums[nextIdx] 为 ”将要产生的新组合“之和
        return self.curSum + nums[self.nextIdx] < (other.curSum + nums[other.nextIdx])


# 算法入口
def getResult():
    nums.sort()

    # pq = queue.PriorityQueue()
    pq = []

    # 空组合的和为0, 将要加入的新元素是nums[0], 即索引0的元素,其将要产生的新组合之和为 0 + nums[0]
    c = CombineModel(0, 0)

    for _ in range(0, m):
        # 打印第 i 小组合
        print(c.curSum + nums[c.nextIdx])

        # c是当前最小组合模型,最小的组合模型指的是将要产生的新组合之和在对应轮次中最小
        # 如果当前组合模型c还有可合入的下一个元素,即c.nextIdx + 1 < n, 则说明可以基于当前组合模型产生一个新组合
        if c.nextIdx + 1 < n:
            # 基于当前组合模型产生的新组合,也是本轮最小的组合,即第 i 小组合
            # pq.put(CombineModel(c.curSum + nums[c.nextIdx], c.nextIdx + 1))
            heapq.heappush(pq, CombineModel(c.curSum + nums[c.nextIdx], c.nextIdx + 1))

            # 当前组合需要更新nextIdx后,重新加入优先队列
            c.nextIdx += 1
            # pq.put(c)
            heapq.heappush(pq, c)

        # 取出优先队列中最小组合(注意这里的最小,指的是基于当前组合,将要产生的新组合之和最小)
        # c = pq.get()
        if len(pq) > 0:
            c = heapq.heappop(pq)


# 算法调用
getResult()

免责声明:

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

0

评论0

站点公告

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