前言:
秋招在即,我们到底要刷多少题?才能进大厂
作者介绍:
????作者:偷偷敲代码的青花瓷????????
文章目录
- ✨求正数数组的最小不可组成和 ---(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时截至
注意:
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
具体代码如下:
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
计算流程:
具体代码如下:
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日是星期几,找到五月的最后一个星期一
这里给定年月日判断这天是星期几
我们使用一个数学公式公式:蔡勒公式 百度百科
如何找到参考点解决了,那么第二个问题,
如何通过这个参照点(当月的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月的最后一个星期一:
总结:
- 其中固定日期的节日,直接输出即可
- 其中非固定日期的节日,我们通过找到某个基准日期是星期几,进行推算
- 先 利用 公式 推导出 这个某个月 1号 是星期几
- 已知 1 号 是星期 w ,怎么才能找到 第 n 个星期 e
= 1 + (n - 1)7 + (7 - w + e) %7- 已知6月1日是星期w,怎么找到5月的最后一个星期一
32 - (w == 1?7 : w-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();
}
}
}
输出结果:
✨分解因数 — (短除法,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
解题思路:
这道题思路很简单,运用我们小学所学的知识,分解因数
所以: 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个参数是需要进行拼接的元素,可以是多个字符串,可以是字符串数组,可以是字符串队列。
✨✨总结
刷题不能速成,需要慢慢的一步一个脚印的去刷,总结好每一道题的知识点,好好编写每一步代码。,当我们看题的时候,我们先好好思考一下这道题如何下手,慢慢的去推导,再去写代码,不要直接题意都没明白就开始上手写
算法的积累需要我们一点一点的去积累,我们刷好每一道题,多刷题,我相信在秋招的时候我们定会大放光彩! 所以我们共同加油!!!!!✨✨✨
“种一颗树最好的是十年前,其次就是现在”
所以,
“让我们一起努力吧,去奔赴更高更远的山海”