算法刷题整理合集(五)
本篇博客旨在记录自已的算法刷题练习成长,里面注有详细的代码注释以及和个人的思路想法,希望可以给同道之人些许帮助。本人也是算法小白,水平有限,如果文章中有什么错误或遗漏之处,望各位可以在评论区指正出来,各位共勉💪。
文章目录
- 1、数字诗意
- 2、摆花
- 3、接水问题
- 4、合唱队形
- 5、聪明的猴子
- 6、求先序排列
- 7、数字游戏
1、数字诗意
在诗人的眼中,数字是生活的韵律,也是诗意的表达。
小蓝,当代顶级诗人与数学家,被赋予了"数学诗人"的美誉。他擅长将冰冷的数字与抽象的诗意相融合,并用优雅的文字将数学之美展现于纸上。
某日,小蓝静坐书桌前,目光所及,展现着个数字,它们依次为a1,a2,…,an,熠熠生辉。小蓝悟到,如果一个数能够以若干个(至少两个)连续的正整数相加表示,那么它就蕴含诗意。例如,数字 6 就蕴含诗意,因为它可以表示为1十2+3。而 8则缺乏诗意,因为它无法用连续的正整数相加表示。
小蓝希望他面前的所有数字都蕴含诗意,为此,他决定从这n个数字中删除一部分。请问,小蓝需要删除多少个数字,才能使剩下的数字全部蕴含诗意?
用例规模:
对于 30% 的评测用例,1 ≤ n ≤ 103,1 ≤ ai ≤ 103。
对于所有评测用例,1 ≤ n ≤ 2×105,1 ≤ai ≤ 1016。
解题代码:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
// 一个数为至少两个连续整数相加得到的数,它一定不是2的幂。
int count = 0;
for (int i = 1; i <= n; i++) {
long j = sc.nextLong();
while (j % 2 ==0 && j > 1) {
j /= 2;
}
if (j == 1){
count++;
}
}
System.out.println(count);
}
}
2、摆花
小明的花店新开张,为了吸引顾客,他想在花店的门口摆上一排花,共 m 盆。通过调查顾客的喜好,小明列出了顾客最喜欢的 n 种花,从 1 到 n 标号。为了在门口展出更多种花,规定第 i 种花不能超过 ai 盆,摆花时同一种花放在一起,且不同种类的花需按标号的从小到大的顺序依次摆列。
试编程计算,一共有多少种不同的摆花方案。
用例规模:
0 < n ≤ 100,0 < m ≤ 100,0 ≤ ai ≤ 100。
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
int[][] dp = new int[n+1][m+1]; // n为花的种类,m为总共盆数
int[] a = new int[n+1]; // 用来表示每种花摆多少盆
for (int i = 1; i <= n; i++) {
a[i] = sc.nextInt(); // 输入每种花由多少盆
}
// 表示第 i 种花摆0盆为1种方案
for (int i = 0; i <= n; i++) {
dp[i][0] = 1;
}
for (int i = 1; i <= n; i++) { // 遍历花的种类
for (int j = 1; j <= m; j++) { // 遍历花的总盆数
for (int k = 0; k <= j&&k <= a[i]; k++) { // 遍历每种花的盆数
dp[i][j] += dp[i-1][j-k];
dp[i][j] %= (int)1e6+7; // 取模
}
}
}
System.out.println(dp[n][m]);
}
}
3、接水问题
学校里有一个水房,水房里一共装有 m 个龙头可供同学们打开水,每个龙头每秒钟的供水量相等,均为 1。
现在有 n 名同学准备接水,他们的初始接水顺序已经确定。将这些同学按接水顺序从1到 n编号,i号同学的接水量为 wi。接水开始时1 到 m 号同学各占一个水龙头,并同时打开水龙头接水。当其中某名同学j完成其接水量要求 wi后,下一名排队等候接水的同学k马上接替j同学的位置开始接水。这个换人的过程是瞬间完成的,且没有任何水的浪费。即 j同学第 x 秒结束时完成接水,则 k 同学第 :x+1 秒立刻开始接水。若当前接水人数 n’不足 m,则只有 n’个龙头供水,其它 m-n’个龙头关闭。
现在给出 n 名同学的接水量,按照上述接水规则,问所有同学都接完水需要多少秒。
输入描述:
第1行2个整数n和m用一个空格隔开,分别表示接水人数和龙头个数。第2行n个整数 w1、w2、…,wn每两个整数之间用个空格隔开,wi表示i号同学的接水量。
输出描述:
输出只有一行,1个整数,表示接水所需的总时间。
解题代码:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 定义人数、水龙头,每名同学的节水量
int n = sc.nextInt();
int m = sc.nextInt();
int[] w = new int[n];
for (int i = 0; i < n; i++) {
w[i] = sc.nextInt();
}
sc.close();
int t=m; //索引,下一位同学的序号
// 所有人都接上水了
while (t < n){
int min = 10000; // 求用时最少的水龙头
int k = 0; // 索引,记录哪一号的水龙头
// 遍历找到接水量最少的学生
for (int i = 0; i < m; i++) {
if (w[i] < min){
min = w[i];
k = i;
}
}
// 接水量最少的同学,接完后由第n个人接上,直到所有人接完
w[k] += w[t];
t++;
}
// 求时间最久的水龙头,也就是最后一名同学接完的时间
int max = 0;
// 到这里,所有人已经接上水了,找到接水量最多的那人,他接完的时间就是所需时间
for (int i = 0; i < m; i++) {
if (w[i] > max) {
max = w[i];
}
}
System.out.println(max);
}
}
4、合唱队形
N 位同学站成一排,音乐老师要请其中的 (N−K) 位同学出列,使得剩下的 K 位同学排成合唱队形。
合唱队形是指这样的一种队形:设 K 位同学从左到右依次编号为 1,2,⋯K,他们的身高分别为 T1,T2,⋯,TK, 则他们的身高满足 T1<⋯<Ti > Ti+1 > ⋯ > TK(1≤i≤K)。
你的任务是,已知所有 N 位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。
输入描述:
输入两行。
第一行是一个整数 N (2≤N≤100),表示同学的总数。
第二行有 n 个整数,用空格分隔,第 i 个整数 Ti(130 ≤ Ti ≤ 230) 是第 i 位同学的身高(厘米)。
输出描述:
输出一个整数,就是最少需要几位同学出列。
解题代码:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int N = sc.nextInt();
int[] arr = new int[N];
int[] zx = new int[N]; // 每一个为中心的个数
for (int i = 0; i < N; i++) {
arr[i] = sc.nextInt();
}
// 用来存储左右符合条件的个数
int[] left = new int[N];
int[] right = new int[N];
// 遍历整个数组,计算以每个人为中心的左边有几个符合条件
for (int i = 0; i < N; i++) {
left[i] = 0; // 初始设为0
for (int j = 0; j < i; j++) {
if(arr[j] < arr[i]) {//如果目前的数比初始的数要小
left[i]=Math.max(left[i], left[j]+1);
}
}
}
//遍历整个数组,算出以每个人为中心的右边有几个符合条件
for(int i=N-1;i>=0;i--) {
right[i]=0;//初始设为0
for(int j=N-1;j>=i;j--) {
if(arr[j] < arr[i]) {//如果目前的数比初始的数要小
right[i]=Math.max(right[i], right[j]+1);
}
}
}
for(int i=0;i<N;i++) {
//每一个数为中心的符合条件的个数
zx[i]=left[i]+right[i]+1;
}
int max=0;
//取出其中的最大值,也就是需要最少同学出列的数
for(int i=0;i<N;i++) {
if(max<zx[i]) {
max=zx[i];
}
}
System.out.println(N-max);
}
}
5、聪明的猴子
在一个热带雨林中生存着一群猴子,它们以树上的果子为生。昨天下了一场大雨,现在雨过天晴,但整个雨林的地表还是被大水淹没着,部分植物的树冠露在水面上。猴子不会游泳,但跳跃能力比较强,它们仍然可以在露出水面的不同树冠上来回穿梭,以找到喜欢吃的果实。
现在,在这个地区露出水面的有 N 棵树,假设每棵树本身的直径都很小,可以忽略不计。我们在这块区域上建立直角坐标系,则每一棵树的位置由其所对应的坐标表示(任意两棵树的坐标都不相同)
在这个地区住着的猴子有 M个,下雨时,它们都躲到了茂密高大的树冠中,没有被大水冲走。由于各个猴子的年龄不同、身体素质不同,它们跳跃的能力不同。有的猴子跳跃的距离比较远(当然也可以跳到较近的树上),而有些猴子跳跃的距离就比较近。这些猴子非常聪明,它们通过目测就可以准确地判断出自己能否跳到对面的树上。
现已知猴子的数量及每一个猴子的最大跳跃距离,还知道露出水面的每一棵树的坐标,你的任务是统计有多少个猴子可以在这个地区露出水面的所有树冠上觅食。
输入描述:
第 1 行为一个整数,表示猴子的个数 M(2≤M≤500);
第 2 行为 M 个整数,依次表示猴子的最大跳跃距离(每个整数值在 1∼1000之间);
第 3 行为一个整数表示树的总棵数 N(2≤N≤1000);
第 4 行至第 N+3 行为 NN 棵树的坐标(横纵坐标均为整数,范围为:−1000∼1000)。
(同一行的整数间用空格分开)
输出描述:
输出一个整数,表示可以在这个地区的所有树冠上觅食的猴子数。
解题代码:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;
public class Main {
static int n,m;
static int[]father;
static int[]arr;
static int[]x;
static int[]y;
static List<Node>list=new ArrayList<>();
static class Node{
int x;
int y;
double k;
Node(int xx,int yy,double kk){
x=xx;
y=yy;
k=kk;
}
}
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
m=sc.nextInt();
arr=new int[m];
for(int i=0;i<m;i++) {
arr[i]=sc.nextInt();
}
n=sc.nextInt();
x=new int[n];
y=new int[n];
for(int i=0;i<n;i++) {
x[i]=sc.nextInt();
y[i]=sc.nextInt();
}
for(int i=0;i<n-1;i++) {
for(int j=i+1;j<n;j++) {
//x x坐标 y y坐标
//i 第一个
//j 第二个
double xx=Math.sqrt(Math.pow(x[i]-x[j],2)+Math.pow(y[i]-y[j], 2));
list.add(new Node(i,j,xx));
}
}
Collections.sort(list,(o1,o2)->Double.compare(o1.k,o2.k));
father=new int[n];
for (int i = 0; i <n; i++) {
father[i]=i;
}
int num=0;
double max=0;
for(Node tep:list) {
int u=find(tep.x);
int v=find(tep.y);
if(u!=v) {
father[v]=u;
max=Math.max(max, tep.k);
num++;
if(num==n-1) {
break;
}
}
}
int ans=0;
for(int i=0;i<m;i++) {
if(arr[i]>=max) {
ans++;
}
}
System.out.println(ans);
}
public static int find(int x) {
if(father[x]==x) {
return x;
}
return father[x]=find(father[x]);
}
}
6、求先序排列
给出一棵二叉树的中序与后序排列。求出它的先序排列。(约定树结点用不同的大写字母表示,长度 ≤8)。
输入描述:
输入两行,均为大写字母组成的字符串,表示一棵二叉树的中序与后序排列。
输出描述:
输出一行,表示一棵二叉树的先序。
解题代码:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
String inorder = scan.nextLine(); // 中序遍历
String postorder = scan.nextLine(); // 后序遍历
// 构建先序遍历
String preorder = buildPreorder(inorder, postorder);
// 输出结果
System.out.println(preorder);
scan.close();
}
private static String buildPreorder(String inorder, String postorder) {
if (inorder.isEmpty() || postorder.isEmpty()) {
return "";
}
// 后序遍历的最后一个字符是根节点
char root = postorder.charAt(postorder.length() - 1);
int rootIndex = inorder.indexOf(root); // 找到根节点在中序遍历中的位置
// 分割中序遍历为左子树和右子树
String inorderLeft = inorder.substring(0, rootIndex);
String inorderRight = inorder.substring(rootIndex + 1);
// 分割后序遍历为左子树和右子树
String postorderLeft = postorder.substring(0, rootIndex);
String postorderRight = postorder.substring(rootIndex, postorder.length() - 1);
// 先序遍历为根节点 + 左子树的先序遍历 + 右子树的先序遍历
return root + buildPreorder(inorderLeft, postorderLeft) + buildPreorder(inorderRight, postorderRight);
}
}
7、数字游戏
小 K 同学向小 P 同学发送了一个长度为 8 的 01 字符串来玩数字游戏,小 P 同学想要知道字符串中究竟有多少个 1。
注意:01 字符串为每一个字符是 0 或者 1 的字符串,如 “101”(不含双引号)为一个长度为 3 的 01 字符串。
输入描述:
输入只有一行,一个长度为 8 的 01 字符串 s。
输出描述:
输出只有一行,包含一个整数,即 01 字符串中字符 1 的个数。
解题代码:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String s=sc.nextLine();
char[] c=s.toCharArray();
int cnt=0;
for(int i=0;i<c.length;i++){
if(c[i]=='1'){
cnt++;
}
}
System.out.println(cnt);
}
}
有帮助的话,希望可以点赞❤️+收藏⭐,谢谢各位大佬~~✨️✨️✨️