开始春招寻找实习的过程中,遇到了很多场笔试。以赛代练,总结在笔试中经常出现的题目。
招行Fintech笔试 4.29
训练营分组 (dp思路+二分加速)
dp是类似于打家劫舍的思路,维护了区间的最值,转移方程是考虑是否选取当前点。
其实感觉笔试题目在看到解析的时候并不太难,但是很多时候思路被卡死的比较多。这个题目当时考虑了贪心的思路,每次都贪心的选取出来最大的一组,然后把这个过程重复两次。但是似乎还是存在错误。其实采用一个额外的dp数组维护就可以。dp[i]维护了区间nums[i, n-1]这个区间上,可以参与的最大人数。而对于转移方程的定义,分别考虑 i 这个位置是否参与选择,类似于打家劫舍的思路。使用二分法找到,选取这个 i 这个点以后右侧的最大点,然后计算长度,并且和不考虑这个点,也就是dp[i+1]比对,得到最大的dp[i]。
而我们要求选择两个区间,因此就是假设选择当前的数字和右端点的dp的最大值。
输入:
5 2
20
3
16
9
5
输出:
3
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int k = sc.nextInt();
int[] a = new int[n];
for(int i = 0;i<n;i++){
a[i] = sc.nextInt();
}
int ans = 0;
Arrays.sort(a);
int[] dp = new int[n+1];
for(int i = n-1;i>=0;i--){
// 这个index是无法满足条件的最小值。
int index = bin(a, a[i]+k);
// 分别是不选取当前点dp[i+1]和选取当前点index-i
dp[i] = Math.max(dp[i+1], index-i);
// 选取当前的,加上dp[index]表示以index为起点的最大值
ans = Math.max(ans, index-i+dp[index]);
}
System.out.println(ans);
}
private static int bin(int[] a, int x){
int n = a.length;
int l = 0;
int r = n-1;
while(l<=r){
int mid = (l+r)/2;
if (a[mid]<x){
l = mid+1;
}else if (a[mid] == x){
l = mid+1;
}else{
r = mid-1;
}
}
return l;
}
}
欢送回家 (dp,枚举最后一个选择的切分位置)
数据量不是很大,因此我们想到的方法dp可以支持到3次的循环。首先很显然我们在设计dp的时候就需要考虑到两维的属性,包括了用户和车辆数。这题目的破题点在于,枚举最后一个车辆的承载用户数量,我们很容易发现,计算一辆车在一个区间内的浪费的空间是非常容易的。因此我们枚举最后一辆车的浪费空间即可。
dp[i][j]表示考虑到第 i 个用户的时候,已经使用了 j 辆车的情况下,最小的浪费。转移方程dp[i][j] = Math.min(dp[i][j], dp[k][j-1]+xmax[k+1][i]*(i-k)-presum[i+1]+presum[k+1]);
也就是前k用户使用了j-1辆车,后面的[k+1, i]的用户使用一辆车。
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int g = sc.nextInt();
int[] a = new int[n];
int max = 0;
int[][] dp = new int[n][g+1];
int sum = 0;
int[] presum = new int[n+1];
// 得到一辆大巴车的浪费情况,baseline
for(int i = 0;i<n;i++){
a[i] = sc.nextInt();
sum += a[i];
presum[i+1] = presum[i]+a[i];
max = Math.max(max, a[i]);
dp[i][1] = max*(i+1)-sum;
}
// xmax表示从i开始到j结束的最大值
int[][] xmax = new int[n][n];
for (int i = 0;i<n;i++){
xmax[i][i] = a[i];
for (int j = i+1;j<n;j++){
xmax[i][j] = Math.max(a[j], xmax[i][j-1]);
}
}
// 转移方程需要考虑到,最后一次分割
for(int i = 0;i<n;i++){
for (int j = 2;j<=Math.min(g,i+1);j++){
dp[i][j] = dp[i][j-1];
// 最后一辆车负责接送从k+1开始的学生
for(int k = 0;k<i;k++){
//dp[i][j] = Math.min(dp[i][j], dp[k][j-1]+help(a, k+1, i+1));
// xmax[k+1][i]这个区间最大值乘以数量 - 实际的 = 浪费的
dp[i][j] = Math.min(dp[i][j], dp[k][j-1]+xmax[k+1][i]*(i-k)-presum[i+1]+presum[k+1]);
}
}
}
System.out.println(dp[n-1][g]);
}
}