2023大厂笔试模拟练习网站(含题解)
www.codefun2000.com
最近我们一直在将收集到的各种大厂笔试的解题思路还原成题目并制作数据,挂载到我们的OJ上,供大家学习交流,体会笔试难度。现已录入200+道互联网大厂模拟练习题,还在极速更新中。欢迎关注公众号“塔子哥学算法”获取最新消息。
提交链接:
https://codefun2000.com/p/P1090
为了更好的阅读体检,可以查看OJ上的题解。进入提交链接,点击右边菜单栏的"查看塔子哥的题解"
题目内容
塔子哥是一位购物狂人,他经常光顾提瓦特商店。最近,提瓦特商店推出了一项促销活动,有 N N N件商品打折销售。每个商品都有原价和折扣价,而且不同的商品的折扣力度也不同。
塔子哥听说了这个促销活动后非常兴奋,他计划购买尽可能多的商品,同时也希望尽量少地花钱。他掏出了自己的钱包,发现他手头有 X X X元的现金和 Y Y Y张折扣券。
于是塔子哥找到了你,希望你能帮助他计算出在这种情况下他可以购买的最多商品数量以及花费的最少钱数。
输入描述
第一行三个整数,以空格分开,分别表示 N , X , Y N,X,Y N,X,Y。
接下来 N N N行,每行两个整数,以空格分开,表示一个的原价和折扣价。
1 ≤ N ≤ 100 , 1 ≤ X ≤ 5000 , 1 ≤ Y ≤ 50 1 \leq N \leq 100,1 \leq X \leq 5000,1 \leq Y \leq 50 1≤N≤100,1≤X≤5000,1≤Y≤50,每个商品原价和折扣价均介于 [ 1 , 50 ] [1,50] [1,50]之间。
输出描述
一行,两个整数,以空格分开。第一个数字表示最多买几个商品,第二个数字表示在满足商品尽量多的前提下所花费的最少的钱数。
样例 1 1 1
输入
2 5 1
3 1
1 1
输出
2 2
说明:
第一个商品折扣价购入,第二个商品原价购入,可以获得最多的商品数量
2
2
2个。此时消耗
2
2
2元。因此输出
22
2 2
22。
样例 2 2 2
输入
5 10 2
10 6
5 3
7 4
4 3
15 3
输出
3 10
思路
动态规划解决.背包问题
状态:
d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k] 代表在考虑前 i i i 个物品时花费 j j j 元且已经用掉了 k k k 个折扣券时能买到的最多的商品.
这里用 a [ i ] a[i] a[i] 代表第 i i i 个商品的原价, b [ i ] b[i] b[i] 代表折扣价
转移:
使用每个商品更新
d
p
dp
dp数组,考虑每个物品用或不用打折卷,有以下转移:
d
p
[
i
]
[
j
]
[
k
]
=
m
a
x
(
d
p
[
i
]
[
j
]
[
k
]
,
d
p
[
i
−
1
]
[
j
−
a
[
i
]
[
k
]
+
1
,
d
p
[
i
−
1
]
[
j
−
b
[
i
]
]
[
k
−
1
]
+
1
)
dp[i][j][k] = max(dp[i][j][k], dp[i-1][j-a[i][k]+1, dp[i-1][j-b[i]][k-1]+1)
dp[i][j][k]=max(dp[i][j][k],dp[i−1][j−a[i][k]+1,dp[i−1][j−b[i]][k−1]+1)
注意:代码实现时使用了刷表法,就是用 d p [ i − 1 ] [ j ] [ k ] dp[i-1][j][k] dp[i−1][j][k] 去更新 d p [ i ] [ j + a [ i ] ] [ k ] dp[i][j+a[i]][k] dp[i][j+a[i]][k] 和 d p [ i ] [ j + b [ i ] ] [ k + 1 ] dp[i][j+b[i]][k+1] dp[i][j+b[i]][k+1],和上面的式子实质是一样的。
更多具体细节见代码注释
类似题目推荐
leetcode
背包问题Leetcode题单总结-代码随想录
CodeFun2000
P1113 字节跳动-暑期实习-2023.03.24-第二题-元素骰子
P1247 美团-春招-2023.04.23-春招-第三题-农场大亨
P1212 塔子大厂真题模拟赛-第二题-魔法石(Ⅱ)
代码
python
n, x, y = map(int, input().split())
a, b = [0]*n, [0]*n
for i in range(n):
a[i], b[i] = map(int, input().split())
dp = [[[-1]*(y+1) for i in range(x+1)] for j in range(2)] #python使用滚动数组优化空间
dp[1][0][0] = 0 #用-1代表不能达到的状态,dp[1][0][0]代表一个商品没买的状态,使用1是因为滚动数组时采用二进制第0位作为标志,第0个物品的前一个用1表示
ans = [0,0] #答案使用列表存储,便于更新,用python列表元素依次比较的原理更新,将第一维置反后取min代表先按第一维从大到小更新,再按第二维从小到大更新
for i in range(n):
for j in range(x+1):
for k in range(y+1):
dp[i&1][j][k] = -1 #将当前位置的所有点初始化为-1
for j in range(x+1):
for k in range(y+1):
if dp[i&1^1][j][k] == -1: #使用上一次的数组dp[i&1^1]更新当前的dp[i&1],如果是-1说明不可达到,继续遍历下一个点
continue
la = dp[i&1^1][j][k] #之前的dp值
if j <= x-a[i]: #如果还能买得起原价的商品,用原价的商品更新
dp[i&1][j+a[i]][k] = max(dp[i&1][j+a[i]][k], la+1) #因为初始化是-1,所以直接取max即可
ans = min(ans, [-dp[i&1][j+a[i]][k], j+a[i]])
if j <= x-b[i] and k < y: #同上,用折扣价更新
dp[i&1][j+b[i]][k+1] = max(dp[i&1][j+b[i]][k+1], la+1)
ans = min(ans, [-dp[i&1][j+b[i]][k+1], j+b[i]])
dp[i&1][j][k] = max(dp[i&1][j][k], dp[i&1^1][j][k])
print(-ans[0], ans[1])
C++
#include<bits/stdc++.h>
using namespace std;
const int N = 105;
int n, x, y;
int a[N], b[N];
int dp[N][1001][51]; // dp[i][j][k]:前i个物品,买了j元,购买的商品折扣次数为k的最大价值和。
int main()
{
cin >> n >> x >> y;
for(int i = 1 ; i <= n ; i ++) {
cin >> a[i] >> b[i]; // 存储每个商品的原价、折扣价
}
memset(dp,-1, sizeof dp); // 初始化所有状态为不可行
dp[0][0][0] = 0; // 没有购买任何商品的状态,可行,价值为0
int ans = 0, mon = 0; // ans记录达到最大价值,mon记录对应需要的钱数
for(int i = 1 ; i <= n ; i ++) { // 遍历每个商品
for(int j = 0 ; j <= x ; j ++) { // 遍历可以用来购买商品的钱数
for(int k = 0 ; k <= y ; k ++) { // 遍历可以使用的折扣次数
if(dp[i-1][j][k] == -1) continue; // 上一个状态不可达,则当前状态也不可达
if(j <= x - a[i]) { // 可以用原价购买商品,更新状态
dp[i][j+a[i]][k] = max(dp[i][j+a[i]][k], dp[i-1][j][k] + 1); // 选择购买当前商品,状态转移方程
int t = dp[i][j+a[i]][k]; // 当前状态的价值
if(t > ans) ans = t, mon = j+a[i]; // 如果达到了更大的价值,更新记录
else if(t == ans) mon = min(mon, j+a[i]); // 如果达到了同样大的价值,看购买商品的钱数是否更少
}
if(j <= x - b[i] && k != y) { // 可以用折扣价购买商品,更新状态
dp[i][j+b[i]][k+1] = max(dp[i][j+b[i]][k+1], dp[i-1][j][k] + 1); // 选择购买当前商品,状态转移方程
int t = dp[i][j+b[i]][k+1]; // 当前状态的价值
if(t > ans) ans = t, mon = j+b[i]; // 如果达到了更大的价值,更新记录
else if(t == ans) mon = min(mon, j+b[i]); // 如果达到了同样大的价值,看购买商品的钱数是否更少
}
dp[i][j][k] = max(dp[i][j][k], dp[i-1][j][k]); // 不购买当前商品,状态不变
}
}
}
cout << ans << ' ' << mon << endl;
}
Java
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n, x, y;
n = scanner.nextInt();
x = scanner.nextInt();
y = scanner.nextInt();
int[] a = new int[n+1]; // 存储每个商品的原价
int[] b = new int[n+1]; // 存储每个商品的折扣价
for(int i=1; i<=n; i++) {
a[i] = scanner.nextInt();
b[i] = scanner.nextInt();
}
// dp[i][j][k]:前i个物品,买了j元,购买的商品折扣次数为k的最大价值和。
int[][][] dp = new int[n+1][x+1][y+1];
for(int i=0; i<=n; i++) {
for(int j=0; j<=x; j++) {
for(int k=0; k<=y; k++) {
dp[i][j][k] = -1; // 初始化所有状态为不可行
}
}
}
dp[0][0][0] = 0; // 没有购买任何商品的状态,可行,价值为0
int ans = 0, mon = 0; // ans记录达到最大价值,mon记录对应需要的钱数
for(int i=1; i<=n; i++) { // 遍历每个商品
for(int j=0; j<=x; j++) { // 遍历可以用来购买商品的钱数
for(int k=0; k<=y; k++) { // 遍历可以使用的折扣次数
if(dp[i-1][j][k] == -1) {
continue; // 上一个状态不可达,则当前状态也不可达
}
if(j <= x-a[i]) { // 可以用原价购买商品,更新状态
dp[i][j+a[i]][k] = Math.max(dp[i][j+a[i]][k], dp[i-1][j][k] + 1); // 选择购买当前商品,状态转移方程
int t = dp[i][j+a[i]][k]; // 当前状态的价值
if(t > ans) {
ans = t;
mon = j+a[i];
} else if(t == ans) {
mon = Math.min(mon, j+a[i]);
}
}
if(j <= x-b[i] && k != y) { // 可以用折扣价购买商品,更新状态
dp[i][j+b[i]][k+1] = Math.max(dp[i][j+b[i]][k+1], dp[i-1][j][k] + 1); // 选择购买当前商品,状态转移方程
int t = dp[i][j+b[i]][k+1]; // 当前状态的价值
if(t > ans) {
ans = t;
mon = j+b[i];
} else if(t == ans) {
mon = Math.min(mon, j+b[i]);
}
}
dp[i][j][k] = Math.max(dp[i][j][k], dp[i-1][j][k]); // 不购买当前商品,状态不变
}
}
}
System.out.println(ans + " " + mon);
}
}
Go
package main
import (
"fmt"
)
func main() {
var n, x, y int
fmt.Scan(&n, &x, &y)
a := make([]int, n+1) // 存储每个商品的原价
b := make([]int, n+1) // 存储每个商品的折扣价
for i := 1; i <= n; i++ {
fmt.Scan(&a[i], &b[i])
}
dp := make([][][]int, n+1) // 定义三维切片
for i := range dp {
dp[i] = make([][]int, x+1)
for j := range dp[i] {
dp[i][j] = make([]int, y+1)
for k := range dp[i][j] {
dp[i][j][k] = -1 // 初始化所有状态为不可行
}
}
}
dp[0][0][0] = 0 // 没有购买任何商品的状态,可行,价值为0
ans, mon := 0, 0 // ans记录达到最大价值,mon记录对应需要的钱数
for i := 1; i <= n; i++ { // 遍历每个商品
for j := 0; j <= x; j++ { // 遍历可以用来购买商品的钱数
for k := 0; k <= y; k++ { // 遍历可以使用的折扣次数
if dp[i-1][j][k] == -1 { // 上一个状态不可达,则当前状态也不可达
continue
}
if j <= x-a[i] { // 可以用原价购买商品,更新状态
dp[i][j+a[i]][k] = max(dp[i][j+a[i]][k], dp[i-1][j][k]+1) // 选择购买当前商品,状态转移方程
t := dp[i][j+a[i]][k] // 当前状态的价值
if t > ans {
ans, mon = t, j+a[i] // 如果达到了更大的价值,更新记录
} else if t == ans && j+a[i] < mon {
mon = j + a[i] // 如果达到了同样大的价值,看购买商品的钱数是否更少
}
}
if j <= x-b[i] && k != y { // 可以用折扣价购买商品,更新状态
dp[i][j+b[i]][k+1] = max(dp[i][j+b[i]][k+1], dp[i-1][j][k]+1) // 选择购买当前商品,状态转移方程
t := dp[i][j+b[i]][k+1] // 当前状态的价值
if t > ans {
ans, mon = t, j+b[i] // 如果达到了更大的价值,更新记录
} else if t == ans && j+b[i] < mon {
mon = j + b[i] // 如果达到了同样大的价值,看购买商品的钱数是否更少
}
}
dp[i][j][k] = max(dp[i][j][k], dp[i-1][j][k]) // 不购买当前商品,状态不变
}
}
}
fmt.Println(ans, mon)
}
func max(a, b int) int {
if a > b {
return a
}
return b
}
Js
let input = "";
process.stdin.resume();
process.stdin.setEncoding("utf-8");
process.stdin.on("data", (data) => {
input += data;
});
process.stdin.on("end", () => {
const lines = input.trim().split("\n"); //将输入字符串按行分割,转化为数组
const [n, x, y] = lines[0].trim().split(" ").map(Number);
let a = Array(n).fill(0);
let b = Array(n).fill(0);
for (let i = 0; i < n; i++) {
[a[i], b[i]] = lines[i + 1].trim().split(" ").map(Number);
}
let dp = new Array(2);
for (let i = 0; i < 2; i++) {
dp[i] = new Array(x + 1);
for (let j = 0; j < x + 1; j++) {
dp[i][j] = new Array(y + 1).fill(-1);
}
} //初始化dp数组
dp[1][0][0] = 0; //dp[1][0][0]代表一个商品没买的状态
let ans = [0, 0]; // ans[0]记录达到最大价值,ans[1]记录对应需要的钱数
for (let i = 0; i < n; i++) { // 遍历每个商品
let cur = i & 1;
for (let j = 0; j < x + 1; j++) {
for (let k = 0; k < y + 1; k++) {
dp[cur][j][k] = -1; // 滚动数组初始化
}
} //复原dp数组
for (let j = 0; j < x + 1; j++) { // 遍历可以用来购买商品的钱数
for (let k = 0; k < y + 1; k++) { // 遍历可以使用的折扣次数
if (dp[cur ^ 1][j][k] === -1) continue; //如果之前状态不可达,继续遍历下一个点
let la = dp[cur ^ 1][j][k];
if (j <= x - a[i]) { // 可以用原价购买商品,更新状态
dp[cur][j + a[i]][k] = Math.max(dp[cur][j + a[i]][k], la + 1); // 选择购买当前商品,状态转移方程
// 更新ans
if (ans[0] > -dp[cur][j + a[i]][k]){
ans = [-dp[cur][j + a[i]][k], j + a[i]];
}else if (ans[0] == -dp[cur][j + a[i]][k] && ans[1] > j + a[i]){
ans = [-dp[cur][j + a[i]][k], j + a[i]];
}
}
if (j <= x - b[i] && k < y) { // 可以用折扣价购买商品,更新状态 同上
dp[cur][j + b[i]][k + 1] = Math.max(dp[cur][j + b[i]][k + 1], la + 1);
if (ans[0] > -dp[cur][j + b[i]][k + 1]){
ans = [-dp[cur][j + b[i]][k + 1], j + b[i]];
}else if (ans[0] == -dp[cur][j + b[i]][k + 1] && ans[1] > j + b[i]){
ans = [-dp[cur][j + b[i]][k + 1], j + b[i]];
}
}
dp[cur][j][k] = Math.max(dp[cur][j][k], dp[cur ^ 1][j][k]);
}
}
}
console.log(-ans[0] + " " + ans[1]);
}); //将答案数组输出,注意格式