支付宝消费打折
思路
拿到这道题,先想清楚目标是什么?你有 n 件物品,每件只能买一个,有的能用支付宝打九五折,有的不能。你手上有 m 元余额,问最多能买几件?
那既然要买尽可能多的物品,直觉告诉你应该怎么做?——当然是先买便宜的。贵的往后排,能塞几个塞几个。
关键点:怎么算每件物品的实际花费?
- 支持优惠的物品(标记为
1):实际花费 = 原价 × 0.95 - 不支持优惠的物品(标记为
0):实际花费 = 原价
然后把所有物品按实际花费从小到大排序,贪心地从最便宜的开始买,直到余额不够为止。
一个细节:浮点数精度问题
0.95 是个小数,直接用浮点运算可能有精度问题。怎么避免?把所有数都乘以 20,这样:
- 不打折的物品花费 = 原价 × 20
- 打折的物品花费 = 原价 × 19(因为 0.95 × 20 = 19)
- 余额也变成 m × 20
这样全程整数运算,干净利落。
代码
#include <bits/stdc++.h>
using namespace std;
int main(){
int n;
long long m;
scanf("%d%lld", &n, &m);
vector<int> a(n);
for(int i = 0; i < n; i++) scanf("%d", &a[i]);
string s;
cin >> s;
// 乘以 20 避免浮点,打折 ×19,不打折 ×20
vector<long long> cost(n);
for(int i = 0; i < n; i++){
if(s[i] == '1') cost[i] = (long long)a[i] * 19;
else cost[i] = (long long)a[i] * 20;
}
sort(cost.begin(), cost.end());
long long budget = m * 20;
int cnt = 0;
for(int i = 0; i < n; i++){
if(budget >= cost[i]){
budget -= cost[i];
cnt++;
} else break;
}
printf("%d\n", cnt);
return 0;
}
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
long m = sc.nextLong();
int[] a = new int[n];
for (int i = 0; i < n; i++) a[i] = sc.nextInt();
String s = sc.next();
// 乘以 20 避免浮点,打折 ×19,不打折 ×20
long[] cost = new long[n];
for (int i = 0; i < n; i++) {
if (s.charAt(i) == '1') cost[i] = (long) a[i] * 19;
else cost[i] = (long) a[i] * 20;
}
Arrays.sort(cost);
long budget = m * 20;
int cnt = 0;
for (int i = 0; i < n; i++) {
if (budget >= cost[i]) {
budget -= cost[i];
cnt++;
} else break;
}
System.out.println(cnt);
}
}
import sys
input = sys.stdin.readline
n, m = map(int, input().split())
a = list(map(int, input().split()))
s = input().strip()
# 乘以 20 避免浮点,打折 ×19,不打折 ×20
cost = []
for i in range(n):
if s[i] == '1':
cost.append(a[i] * 19)
else:
cost.append(a[i] * 20)
cost.sort()
budget = m * 20
cnt = 0
for c in cost:
if budget >= c:
budget -= c
cnt += 1
else:
break
print(cnt)
const readline = require('readline');
const rl = readline.createInterface({ input: process.stdin });
const lines = [];
rl.on('line', line => lines.push(line));
rl.on('close', () => {
const [n, m] = lines[0].split(' ').map(Number);
const a = lines[1].split(' ').map(Number);
const s = lines[2].trim();
// 乘以 20 避免浮点,打折 ×19,不打折 ×20
const cost = [];
for (let i = 0; i < n; i++) {
if (s[i] === '1') cost.push(a[i] * 19);
else cost.push(a[i] * 20);
}
cost.sort((a, b) => a - b);
let budget = m * 20;
let cnt = 0;
for (let i = 0; i < n; i++) {
if (budget >= cost[i]) {
budget -= cost[i];
cnt++;
} else break;
}
console.log(cnt);
});
复杂度分析
- 时间复杂度:
,瓶颈在排序
- 空间复杂度:
,存每件物品的实际花费
总结
这道题本质就是贪心排序:算出每件物品的实际花费,排个序,从便宜的开始买。唯一值得注意的是九五折带来的浮点精度问题,全部乘 20 转成整数就搞定了。排序贪心是很经典的套路,遇到"最多能选几个"这类问题,优先往这个方向想。



京公网安备 11010502036488号