前言:


​秋招在即,我们到底要刷多少题?才能进大厂​



作者介绍:


????作者:偷偷敲代码的青花瓷????‍????


【Java刷题特辑第三期】——这些经典笔试题,你确定都做过吗?_每日一题






文章目录

  • ​​✨求正数数组的最小不可组成和 ---(DP背包问题思路转化)​​
  • ​​✨有假币 --- (向上整形,数学思维)​​
  • ​​✨最难的问题 --- (字符转换,StringBuilder拼接)​​
  • ​​✨因子个数 --- (对概念的把握)​​
  • ​​✨美国节日 --- (结合数学公式求解)​​
  • ​​✨分解因数 --- (短除法,Sting.valueOf()方法)​​
  • ​​✨✨总结​​






✨求正数数组的最小不可组成和 —(DP背包问题思路转化)


题目描述:


给定一个全是正数的数组arr,定义一下​​arr的最小不可组成和的概念​​: 1.arr的所有非空子集中,把每个子集内的所有元素加起来会出现很多的值,其中最小的记为min,最大的记为max; 2.在区间[min,max]上,如果有一些正数不可以被arr某一个子集相加得到,那么这些正数中最小的那个,就是arr的最小不可组成和; 3,在区间[min,max]上,如果所有的数都可以被arr的某一个子集相加得到,那么max+1是arr的最小不可组成和; 举例: arr = {3,2,5} arr的min为2,max为10,在区间[2,10]上,4是不能被任何一个子集相加得到的值中最小的,所以4是arr的最小不可组成和; arr = {3,2,4} arr的min为2,max为9,在区间[2,9]上,8是不能被任何一个子集相加得到的值中最小的,所以8是arr的最小不可组成和; arr = {3,1,2} arr的min为1,max为6,在区间[2,6]上,任何数都可以被某一个子集相加得到,所以7是arr的最小不可组成和; 请写函数返回arr的最小不可组成和。


题目解析:


arr = {3,2,5} arr的min为2,max为10,在区间[2,10]上,4是不能被任何一个子集相加得到的值中最小的,所以4是arr的最小不可组成和,如果在区间[2,10]内的值,都能被数组arr中的数相加得到,则返回 max + 1


解题思路:


这道题,用01背包问题思路转化,非常的好理解,在前一篇博客当中,我总结了01背包问题的解题思路,非常的详细,图文并茂,如果对01背包问题思路不是很熟悉,这道题就比较麻烦。​​01背包问题详解​​ 建议先花10分钟看01背包问题,看懂后,这道题,就晓得相当的简单!


具体代码如下:

public class Solution {
/**
* 正数数组中的最小不可组成和
* 输入:正数数组arr
* 返回:正数数组中的最小不可组成和
*/
//01背包问题思路转换,arr[]数组里面的值,看做物品容量和物品的价值
//[min,max]里面的值,看做书包的容量
public int getFirstUnFormedNum(int[] arr) {
//首先确定Min和max的区间
int min = Integer.MAX_VALUE;//给定初始化的值
int max = 0;
for(int i = 0;i < arr.length;i++) {
if(min > arr[i]) {
min = arr[i];//更新最小值
}
max += arr[i];
}
//根据01背包问题思路转化,找出状态方程
int dp[] = new int[max+1];//dp[j]表示容量为i的时候,背包能够装入的最大价值
for(int i = 0;i < arr.length;i++) {
for(int j = max;j >= min;j--) {
//如果背包容量小于物品容量这种情况我们直接忽略
if(j >= arr[i]) {
//这里在放当前物品和不放当前物品中取最大值
dp[j] = Math.max(dp[j],dp[j - arr[i]]+arr[i]);
}
}
}
//退出循环,在区间[min,max]中判断 物品容量能不能满足背包容量
//这里就是和01背包问题的不点,在于最后所求的结果不同
for(int i = min;i <= max;i++) {
if(dp[i] != i) {
return i;
}
}
return max+1;

}
}

总结:


DP问题是大厂经常考察的一个点,所以我们当我们遇到DP问题的时候千万别急,先去慢慢找他的状态转移方程,在一步步去推导,如果对DP问题不知道如何下手的同学,可以去看一下我之前写的博客,总计了DP问题的解题思维和一些经典列题希望对你有所帮助:​​数据结构与算法—算法篇之动态规划(一)​​


✨有假币 — (向上整形,数学思维)

题目描述:

居然有假币! 现在猪肉涨了,但是农民的工资却不见涨啊,没钱怎么买猪肉啊。nowcoder这就去买猪肉,结果找来的零钱中有假币!!!可惜nowcoder 一不小心把它混进了一堆真币里面去了。只知道​​假币的重量比真币的质量要轻​​,给你一个天平(天平两端能容纳无限个硬币),请用​​最快的时间​​把那个可恶的假币找出来。

输入描述:
1≤n≤2^30,输入0结束程序。
输出描述:
最多要称几次一定能把那个假币找出来?
示例1
输入
3
12
0
输出
1
3

解题思路:


​平均分三份是最快的方法​​​,​​两份进行称重(对比出三个的重量 )​​​,​​后对最重的那份再次进行称重,直到称重的个数不足2个时则结束​​,获得假币 如果无法平均分3分则余数要么是1要么是2,因为是要最多称几次,n=n/3+1满足每次取最大 分称3份,取两份一样多的过秤,然后把三份中最多的那份继续分,直到硬币剩余0或1时截至


【Java刷题特辑第三期】——这些经典笔试题,你确定都做过吗?_leetcode_02

注意:


​n/3 向上取整 我们可以使用 Math.ceil() 方法​​​​n/3 是 int/int,返回值也是 int​​ ,小数已经丢失了,应该使用:​​(double)n/3​​返回一个浮点型,再作为参数传入 ​​Math.ceil() ​​方法 即:​​Math.ceil((double)n/3)​


具体代码如下:

// write your code here
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while(sc.hasNext()){
int n = sc.nextInt();
//题上说 输入0 结束程序
if(n == 0) break;
int count = 0;//记录次数
while(n >=2) {
n = (int)Math.ceil((double)n/3);//向上取证
count++;
}
System.out.println(count);
}

}
}

✨最难的问题 — (字符转换,StringBuilder拼接)


题目描述:

NowCoder生活在充满危险和阴谋的年代。为了生存,他首次发明了密码,用于军队的消息传递。假设你是军团中的一名军官,需要把​​发送来的消息破译出来​​、并提供给你的将军。​​消息加密的办法是:对消息原文中的每个字母,分别用该字母之后的第5个字母替​​(例如:消息原文中的每个字母A 都分别替换成字母F),其他字符不 变,并且​​消息原文的所有字母都是大写的​​。密码中的字母与原文中的字母对应关系如下。

密码字母:A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

原文字母:V W X Y Z A B C D E F G H I J K L M N O P Q R S T U

输入描述:
输入包括多组数据,每组数据一行,为收到的密文。
密文仅有空格和大写字母组成。

输出描述:
对应每一组数据,输出解密后的明文。
示例1
输入
HELLO WORLD<br/>SNHJ
输出
CZGGJ RJMGY<br/>NICE

解题思路:


核心:​​密码 > 'E' 则:原文= 密码 - 5​​​ 否则: ​​原文 = 密码 + 21​


【Java刷题特辑第三期】——这些经典笔试题,你确定都做过吗?_每日一题_03

【Java刷题特辑第三期】——这些经典笔试题,你确定都做过吗?_算法_04

具体代码如下:

import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while(sc.hasNext()) {
String line = sc.nextLine();
// 用来拼接 字符串
StringBuilder sb = new StringBuilder();
//遍历字符串
for(int i = 0;i < line.length();i++) {
char c = line.charAt(i);
//如果是 空格 直接拼接
if(c == ' ') {
sb.append(" ");
}else {
//前'E'之前 - 5,'E'之后 + 21
sb.append((char)(c >'E'?c-5 : c + 21));
}
}
System.out.println(sb);
}
}
}

✨因子个数 — (对概念的把握)


题目描述:

一个正整数可以分解成一个或多个数组的积。例如​​36=2*2*3*3​​,即包含2和3两个因子。NowCoder最近在研究因子个数的分布规律,现在给出一系列正整数,他希望你开发一个程序输出每个正整数的因子个数。

输入描述:
输入包括多组数据。
每组数据仅有一个整数n (2≤n≤100000)。

输出描述:
对应每个整数,输出其因子个数,每个结果占一行。
示例1
输入
30<br/>26<br/>20
输出
3<br/>2<br/>2

解题思路:


从最小因子2到数字的最大因子数(数字的平方根)开始判断是否能够取余
可以则循环取余直到取余不为0,因子个数+1;否则使用下一个因子计算;
最终整除了各个因子数之后剩余的数字不为1则本身也是一个因子,因此因子数+1


【Java刷题特辑第三期】——这些经典笔试题,你确定都做过吗?_数据_05

计算流程:

【Java刷题特辑第三期】——这些经典笔试题,你确定都做过吗?_数据_06

具体代码如下:

import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while(sc.hasNext()){
int n = sc.nextInt();
int count = 0;
for(int i = 2;i < Math.sqrt(n);i++) {
if(n % i == 0) {
while(n % i == 0) {
n = n/i;
}
count++;
}
}
if(n != 1) {
//不为1则本身也是一个因子,因此因子数+1
count++;
}
System.out.println(count);
}
}
}

✨美国节日 — (结合数学公式求解)

题目描述:

和中国的节日不同,美国的节假日通常是选择某个月的第几个星期几这种形式,因此每一年的放假日期都不相同。具体规则如下:

  • 1月1日:元旦
  • 1月的第三个星期一:马丁·路德·金纪念日
  • 2月的第三个星期一:总统节
  • 5月的最后一个星期一:阵亡将士纪念日
  • 7月4日:美国国庆
  • 9月的第一个星期一:劳动节
  • 11月的第四个星期四:感恩节
  • 12月25日:圣诞节
    现在给出一个年份,请你帮忙生成当年节日的日期。
输入描述:
输入包含多组数据,每组数据包含一个正整数year(2000≤year≤9999)。

输出描述:
对应每一组数据,以“YYYY-MM-DD”格式输出当年所有的节日日期,每个日期占一行。

每组数据之后输出一个空行作为分隔。
示例1
输入
2014
2013
输出
2014-01-01
2014-01-20
2014-02-17
2014-05-26
2014-07-04
2014-09-01
2014-11-27
2014-12-25

2013-01-01
2013-01-21
2013-02-18
2013-05-27
2013-07-04
2013-09-02
2013-11-28
2013-12-25

题目解析:


题目表述很明白,​​难点在于我们要求一个月第N个星期W​​。那么面对这个问题,我们拆解的思路是,首先,​​我们要想找到一个月第N个星期W,一定需要一个参照物,最好的目标当然是这个月的第一天​​。拿到参照物后,我要能得到参照物的星期数,然后就能得到结果了。所以这个题有两个难点:​​判断某个月的1号到底是周几,然后根据这个星期数得到这个月第N个星期W​​。这两个功能写成函数,即可通过反复调用拿到结果。


解题思路:


问题被拆解成如下:
​1.根据(年月日)计算出这天是星期几​​​​2.根据每月的一号是星期几,找到本月的第n个星期x​​​​3.根据6月1日是星期几,找到五月的最后一个星期一​


这里​​给定年月日判断这天是星期几​​我们使用一个数学公式公式:蔡勒公式​百度百科​

【Java刷题特辑第三期】——这些经典笔试题,你确定都做过吗?_leetcode_07

【Java刷题特辑第三期】——这些经典笔试题,你确定都做过吗?_java_08


如何找到参考点解决了,那么第二个问题,​​如何通过这个参照点(当月的1号为星期几)拿到一个月的第N个星期W​​。那么,我们假设要拿到一个月的第一个周五,我们要怎么做呢?一个很简单的思路就是,先看看这个月的1号是周几,然后往后数就行了,假如1号是周四,那么2号就是第一个周五,假如1号是周六,那么7号就是第一个周五。那么,怎么拿到这个向后的天数呢?我们发现,如果所求星期数比1号星期数大,那么直接相减后+1就是那一天了,例如1号周三,我要周五,那么(5-3)+1即可求出第一个周五是3号。那么反过来是所求星期数小,例如1号周三,我要周一,那么显然要先把周一看成周八才行。也就是(8-3)+1。第一个周一是6号。但是这样要判断,所以干脆统统都让它加7以后减,减完后的结果再mod一下7,就能得到结果了。也就是:​​(所求星期数 + 7 - 1号星期数) % 7 + 1​​​。这样我们就拿到了求第一个周几公式。​​随后,我们只需要在这个公式上,加上7 * (n - 1),即刻求出第n个周几。而面对某个月的最后一个周几,我们要做的是拿到下个月的第一天然后往回推即可。​


这里都解决以后,我们还需要来单独处理一下5月的最后一个星期一:

【Java刷题特辑第三期】——这些经典笔试题,你确定都做过吗?_leetcode_09

总结:


  1. 其中固定日期的节日,直接输出即可
  2. 其中非固定日期的节日,我们通过找到某个基准日期是星期几,进行推算
  • 先 利用 公式 推导出 这个某个月 1号 是星期几
  • 已知 1 号 是星期 w ,怎么才能找到 第 n 个星期 e
    = 1 + (n - 1)7 + (7 - w + e) %7
  • 已知6月1日是星期w,怎么找到5月的最后一个星期一
    ​32 - (w == 1?7 : w-1)​
  1. 输入是有多组
    每组输出的最后要多带一个空行


代码如下:

import java.util.Scanner;

/**
* Created with IntelliJ IDEA.
* Description:牛客 美国节日
* User:吴彦祖
* Date: 2022-05-09
* Time: 14:16
*/
public class Demo3 {
//传入 年 月 日 通过蔡勒公式计算当前星期几-->作为参考
public static int day_of_week(int year,int month,int day) {
if(month == 1 || month == 2) {
//如果是1月或者2月 看做 13月和14月 并且年份 -1
month += 12;
year -= 1;
}
int century = year/100;//2064 -- > 20世纪
year = year % 100;// 2064 --> 64
//带入公式 求 当前给定 年月日 是星期几
int week = century / 4 - 2 * century + year + year / 4 + (13 * (month + 1)) / 5 + day - 1;
week = (week % 7 + 7) % 7;//注意对负数的取模运算!如 -12 % 7 不是星期五 而是 星期二
//week = 0 星期天 week =1 星期1
if(week == 0) {
week = 7;
}
return week;
}
/**
* 根据 1号 是星期 w ,求第 count 个 星期 d_of_week(几) 是 几号
* 如 1号为周4 那么 周五 就是 2号
* @param year 年份
* @param month 月份
* @param count 第 n 个 星期
* @param d_of_week 所求星期数
* @return
*/
public static int day_of_demand(int year,int month,int count,int d_of_week) {
//计算 本月 1号 是星期几 --> 作为参考
int week = day_of_week(year,month,1);
// 根据 公式 (所求星期数 + 7 - 1号星期数) % 7 + 1 求出 号数
return 1 + (count - 1)*7 + (7 + d_of_week - week) % 7;

}
//5月的最后一个星期一
//根据 6 月 1 日 星期 w,求五月的最后一个星期一
public static int m(int year) {
int week = day_of_week(year,6,1);
int day = (week == 1 ? 7 : week -1);
return 32 - day;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
int year = sc.nextInt();
//元旦
System.out.printf("%d-01-01\n",year);
//一月的第三个星期一:马丁·路德 金纪念日
System.out.printf("%d-01-%02d\n",year,day_of_demand(year,1,3,1));
//2月的第三个星期一:总统节
System.out.printf("%d-02-%02d\n",year,day_of_demand(year,2,3,1));
// 5月的最后一个星期一:阵亡将士纪念日
System.out.printf("%d-05-%02d\n",year,m(year));
//7月4日:美国国庆
System.out.printf("%d-07-04\n",year);
//9月的第一个星期一:劳动节
System.out.printf("%d-09-%02d\n",year,day_of_demand(year,9,1,1));
//11月的第四个星期四:感恩节
System.out.printf("%d-11-%02d\n",year,day_of_demand(year,11,4,4));
//12月25日:圣诞节
System.out.printf("%d-12-25\n",year);
System.out.println();

}
}
}

输出结果:

【Java刷题特辑第三期】——这些经典笔试题,你确定都做过吗?_每日一题_10

✨分解因数 — (短除法,Sting.valueOf()方法)


题目描述:

所谓因子分解,就是把给定的正整数a,分解成若干个素数的乘积,即 a = a1 × a2 × a3 × … × an,并且 1 < a1 ≤ a2 ≤ a3 ≤ … ≤ an。其中a1、a2、…、an均为素数。 先给出一个整数a,请输出分解后的因子。

输入描述:
输入包含多组数据,每组数据包含一个正整数a(2≤a≤1000000)。

输出描述:
对应每组数据,以“a = a1 * a2 * a3...”的形式输出因式分解后的结果。
示例1
输入
10<br/>18
输出
10 = 2 * 5<br/>18 = 2 * 3 * 3

解题思路:


这道题思路很简单,运用我们小学所学的知识,分解因数


【Java刷题特辑第三期】——这些经典笔试题,你确定都做过吗?_算法_11

所以: 90 = 2 * 3 * 3 * 5


看到短除法后,我们很清楚的知道,要想求出它的每一个质因数,我们需要用质数去试除。90能被2整除,那就拿商继续除以2,除不尽就换3,一直到除到质数为止。基础代码框架类似判断质数,只是被判断的数字在过程中不断被除,最终循环结束的时候,那个被处理过的数字,就是最后一个质因数。
​注意:这里有个坑,题目上没有说明的是:如果出现了质数,需要打印成 13 = 13的形式​


具体代码如下:

import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while(sc.hasNext()) {
int n = sc.nextInt();
List<String> list2 = func(n);
System.out.printf("%d = %s\n",n,String.join(" * ",list2));
}

}
public static List<String> func(int n){
List<String> list = new ArrayList<>();
for (int i = 2; n > 1 && i*i <= n ; i++) {
while (n % i == 0) {
n = n/i;
list.add(String.valueOf(i));
}
}
if(n > 1) {
list.add(String.valueOf(n));
}
return list;
}
}

注意:


这里使用的​​String.valueOf()方法​​:第1个参数是分隔符,第2个参数是需要进行拼接的元素,可以是多个字符串,可以是字符串数组,可以是字符串队列。



✨✨总结


刷题不能速成,需要慢慢的一步一个脚印的去刷,总结好每一道题的知识点,好好编写每一步代码。,当我们看题的时候,我们先好好思考一下这道题如何下手,慢慢的去推导,再去写代码,不要直接题意都没明白就开始上手写
​算法的积累需要我们一点一点的去积累,我们刷好每一道题,多刷题,我相信在秋招的时候我们定会大放光彩! 所以我们共同加油!!!!!✨✨✨​


“种一颗树最好的是十年前,其次就是现在”

所以,

“让我们一起努力吧,去奔赴更高更远的山海”


【Java刷题特辑第三期】——这些经典笔试题,你确定都做过吗?_leetcode_12