小红购物

[题目链接](https://www.nowcoder.com/practice/d4e0c4c4e5154787b7ddc9141d64deda)

思路

本题的核心是:对每件商品,在所有适用的优惠券中选一张折扣最大的使用。

优惠券 对价格为 的商品适用,当且仅当 ,此时可以减去 。每件商品最多用一张券,每种券不限次数。

贪心 + 排序 + 二分

关键观察:对于价格为 的商品,所有满足 的优惠券都可以使用,我们只需要找到其中 最大的那张即可。

做法

  1. 将优惠券按门槛 从小到大排序
  2. 构造一个前缀最大折扣数组 :表示前 张优惠券中折扣的最大值。即
  3. 对于每件价格为 的商品,二分查找门槛数组中最后一个 的位置 。若存在,最优折扣就是 ,商品实际花费 ;否则无券可用,花费

样例演示

商品价格:,优惠券:

排序后优惠券按门槛:,前缀最大折扣:

  • 商品 :二分查找 的门槛,不存在,花
  • 商品 :二分查找 的最右位置为 ,花
  • 商品 :二分查找 的最右位置为 ,花

总花费:

复杂度

  • 时间复杂度:,排序优惠券 ,每件商品二分查找
  • 空间复杂度:,存储商品价格和优惠券信息。

代码

#include <bits/stdc++.h>
using namespace std;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, m;
    cin >> n >> m;
    vector<long long> p(n);
    for(int i = 0; i < n; i++) cin >> p[i];

    vector<pair<long long,long long>> coupons(m);
    for(int i = 0; i < m; i++) cin >> coupons[i].first >> coupons[i].second;

    sort(coupons.begin(), coupons.end());

    vector<long long> thresh(m), maxDisc(m);
    for(int i = 0; i < m; i++){
        thresh[i] = coupons[i].first;
        maxDisc[i] = coupons[i].second;
        if(i > 0) maxDisc[i] = max(maxDisc[i], maxDisc[i-1]);
    }

    long long ans = 0;
    for(int i = 0; i < n; i++){
        int lo = 0, hi = m - 1, pos = -1;
        while(lo <= hi){
            int mid = (lo + hi) / 2;
            if(thresh[mid] <= p[i]){
                pos = mid;
                lo = mid + 1;
            } else {
                hi = mid - 1;
            }
        }
        if(pos >= 0){
            ans += p[i] - maxDisc[pos];
        } else {
            ans += p[i];
        }
    }
    cout << ans << endl;
    return 0;
}
import java.util.*;
import java.io.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        int n = Integer.parseInt(st.nextToken());
        int m = Integer.parseInt(st.nextToken());

        long[] p = new long[n];
        st = new StringTokenizer(br.readLine());
        for (int i = 0; i < n; i++) {
            p[i] = Long.parseLong(st.nextToken());
        }

        long[][] coupons = new long[m][2];
        for (int i = 0; i < m; i++) {
            st = new StringTokenizer(br.readLine());
            coupons[i][0] = Long.parseLong(st.nextToken());
            coupons[i][1] = Long.parseLong(st.nextToken());
        }

        Arrays.sort(coupons, (a, b) -> Long.compare(a[0], b[0]));

        long[] thresh = new long[m];
        long[] maxDisc = new long[m];
        for (int i = 0; i < m; i++) {
            thresh[i] = coupons[i][0];
            maxDisc[i] = coupons[i][1];
            if (i > 0) maxDisc[i] = Math.max(maxDisc[i], maxDisc[i - 1]);
        }

        long ans = 0;
        for (int i = 0; i < n; i++) {
            int lo = 0, hi = m - 1, pos = -1;
            while (lo <= hi) {
                int mid = (lo + hi) / 2;
                if (thresh[mid] <= p[i]) {
                    pos = mid;
                    lo = mid + 1;
                } else {
                    hi = mid - 1;
                }
            }
            if (pos >= 0) {
                ans += p[i] - maxDisc[pos];
            } else {
                ans += p[i];
            }
        }
        System.out.println(ans);
    }
}