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

前言:

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


作者介绍:

作者:偷偷敲代码的青花瓷‍
作者的Gitee:代码仓库
系列文章推荐:
1.【Java刷题特辑第一期】
2.【Java刷题特辑第二期】
✨✨秋招在即,我们究竟要如何刷题,要刷多少题才能进大厂?在此,我想说:我们不必担心前路如何,走好每一步脚印,打好每一步基础,在将来笔试场上,用尽我们所学的算法,所总结的知识,我相信一定能在千万人中,旗开得胜,大放光彩!
我和大家一样都是热爱编程✨,很高兴能在此和大家分享知识,希望在分享知识的同时,能和大家一起共同进步,取得好成绩如果有错误❌,欢迎指正哟,咋们废话不多说,跟紧步伐,开始学习吧~

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


文章目录

  • ✨求正数数组的最小不可组成和 ---(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刷题特辑第三期】——这些经典笔试题,你确定都做过吗?_第2张图片

注意:

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
SNHJ 输出 CZGGJ RJMGY
NICE

解题思路:

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

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

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

具体代码如下:

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
26
20 输出 3
2
2

解题思路:

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

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

计算流程:
【Java刷题特辑第三期】——这些经典笔试题,你确定都做过吗?_第6张图片

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

具体代码如下:

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刷题特辑第三期】——这些经典笔试题,你确定都做过吗?_第8张图片
【Java刷题特辑第三期】——这些经典笔试题,你确定都做过吗?_第9张图片

如何找到参考点解决了,那么第二个问题,如何通过这个参照点(当月的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刷题特辑第三期】——这些经典笔试题,你确定都做过吗?_第10张图片
总结:

  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刷题特辑第三期】——这些经典笔试题,你确定都做过吗?_第11张图片

✨分解因数 — (短除法,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
18 输出 10 = 2 * 5
18 = 2 * 3 * 3

解题思路:

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

【Java刷题特辑第三期】——这些经典笔试题,你确定都做过吗?_第12张图片
所以: 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 list2 = func(n);
        System.out.printf("%d = %s\n",n,String.join(" * ",list2));
        }
        
    }
    public static List func(int n){
        List 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刷题特辑第三期】——这些经典笔试题,你确定都做过吗?_第13张图片

你可能感兴趣的:(算法篇,Java刷题笔记,java,算法,每日一题,leetcode)