文章目录
- 算法入门五:数组:
- 引言:
- 数组:
- 1.洛谷 p1428 Java 小鱼比可爱
- 2.洛谷 p1427 Java 小鱼的数字游戏
- 3.洛谷 p5727 Java 冰雹猜想
- 4.洛谷 p1047 Java 校门外的树
- 5.洛谷 p5728 Java 旗鼓相当的对手
- 6.洛谷 p5769 Java 工艺品
- 7.洛谷 p2550 Java 彩票摇奖
- 8.洛谷 p1554 Java 梦中的统计
- 9.洛谷 p1614 Java 爱与愁的心痛
- 10洛谷 p2911 Java Bovine Bones G
- 11.洛谷 p1161 Java 开灯
- 12.洛谷 p5731 Java 蛇形方阵
- 13.洛谷 p5732 Java 杨辉三角
- 14.洛谷 p1789 Java 插火把 (较难理解)
- 15.洛谷 p1319 Java 压缩技术
- 16.洛谷 p2615 Java 神奇的幻方
- 17.洛谷 p2141 Java 珠心算测试
算法入门五:数组:
引言:
在小组的安排下,这个暑假我们统一学习三个星期的算法,在这里让我感觉最大的感受是,前面有人引你入门真的好,可以少走很多的冤枉路,可以快速增长自己的能力和水平,但后续就要求自己自主的学习,因为别人的路线毕竟是别人的,不可能完全的适合自己的,但入门系列是每一个程序员或爱好算法的同学的必经之路,入门系列就是基础阶段,只有将基础打好,后面才能走的更远更好。在完成入门系列专项训练之后,我将自己做的题和做题过程中遇到的问题及解题思路总结成笔记。
记得上数据结构的时候老师说过,数据结构好比是武林高手的内功,往往刚修炼的时候内功比较的弱,但当我们持之以恒的练习,我们终将练就一身内功成就宗师。加油!
数组:
计算机运算速度很快,一秒钟可以处理成千上万的数据。之前的例子都是读取一个数据后立刻对这些数据进行处理,然后再也不需要用到这些数据了;有时候,我们读入数据后还需要将这些数据保存下来,便于以后再次使用。如果保存个别几个数据,可以设立几个变量存储;但是如果要存储成千上万个数据,总不能定义成千上万个变量吧。
1.洛谷 p1428 Java 小鱼比可爱
AC代码:
import java.util.Arrays;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
//鱼的总数
int fishSum = scanner.nextInt();
//六头鱼的可爱程度
int[] arr = new int[fishSum];
for (int i = 0; i < fishSum; i++) {
arr[i] = scanner.nextInt();
}
//比较可爱值
int[] cute = new int[arr.length];
//遍历所有鱼
for (int i = 0; i < arr.length; i++) {
//与它左边的鱼比较
for (int j =i-1; j >= 0; j--) {
if (arr[i] > arr[j]){
cute[i]++;
}
}
}
//输出得到的可爱值列表
// System.out.println(Arrays.toString(cute));
for (int i = 0; i < fishSum; i++) {
System.out.print(cute[i]+" ");
}
}
}
本题重点:
- 需要定义两个数组,一个是输入鱼的可爱程度数据数组,一个是存放比较之后鱼的可爱程度比左边可爱数量的数组
- 双层for循环,一层遍历每一组数据,一层遍历当前🐟之前的🐟,与之比较,计数。
2.洛谷 p1427 Java 小鱼的数字游戏
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int[] arr = new int[100];
int middle = 0;
//输入数据
for (int i = 0; ; i++) {
arr[i] = sc.nextInt();
if (arr[i] == 0){//当遇到0的时候就结束输入,并记下当前的i值,方便输出
middle = i;
break;
}
}
//遍历从middle - 1 --- 0之间的左右数据,为什么方向遍历,是为了倒着输出这一串数据。
for (int i = middle-1; i >= 0 ; i--) {
System.out.print(arr[i]+" ");
}
}
}
3.洛谷 p5727 Java 冰雹猜想
import java.util.Arrays;
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[1000];
int count = 1;//中间变量count,看最后需要多少步
if(n == 1){//当输入的n等于1时
System.out.println("1");
}else{//当输入的n不等于1时
while(n != 1){//题中的操作最终的结果就是n = 1;所以将n != 1作为循环条件,当n == 1时结束。
arr[count] = n;//将每一次的得到的值存入数组之中。
if(n % 2 == 0){//偶数时
n = n / 2;
}else{//奇数时
n = n * 3 + 1;
}
count++;//计数:有多少条数据
}
arr[count] = 1;//while循环的条件就是n != 1;所以当n == 1时没有存入数组,需要单独存进去
//遍历得到想要的数据。
for (int i = count; i > 0; i--) {
System.out.print(arr[i]+" ");
}
}
}
}
4.洛谷 p1047 Java 校门外的树
AC代码:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int l = sc.nextInt();
int m = sc.nextInt();
int[] a = new int[10001];//因为 1 <= l <=10000;
int[] b = new int[10001];
int n = 2*m;
//给各个区段的首尾进行赋值
for(int i=0; i<n; i+=2) {
b[i] = sc.nextInt();//首节点
b[i +1] = sc.nextInt();//尾节点
}
//给整个区间内的点进行赋值
for(int i=0; i<=l; i++) {
a[i] = 1;
}
int r,s;
for(int i=0; i<n; i+=2) {
r = b[i]; //区间起始点
s = b[i+1]; //区间终点
for(int j=r; j<=s; j++) {//把数组b各个区间内元素在数组a中映射为0
a[j] = -1; //将区段内符合的带你进行赋值标记
}
}
int k = 0;
for(int i=0; i<=l; i++) {
if(a[i] != -1) {
k++; //用来统计非0的个数,即不在数组b区间内的数的个数
}
}
System.out.println(k);
}
}
本题重点:
- 使用标记法,先将区间内所有的数据进行重置为1,然后遍历树木要移走的区间,若为1就变成-1,若不为1则不动,最后还为1的部分就是没有动的部分。
5.洛谷 p5728 Java 旗鼓相当的对手
AC代码:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int count = 0;//计数使用
int[][] a = new int[1100][5];//每位同学的各科成绩
int[] b = new int[1100];//每位同学的总成绩
//给每位同学的各科成绩和总成绩进行赋值操作
for (int i = 0; i < n; i++) {
a[i][0] = sc.nextInt();
a[i][1] = sc.nextInt();
a[i][2] =sc.nextInt();
b[i] =a[i][0] + a[i][1] + a[i][2];
}
//循环时,不能自己和自己比,所以第二个变量要从第一个变量 + 1+1 开始。
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
if(Math.abs(a[i][0] - a[j][0]) <= 5 && Math.abs(a[i][1] - a[j][1]) <= 5 && Math.abs(a[i][2] - a[j][2]) <= 5 && Math.abs(b[i] - b[j]) <= 10){//记得要取绝对值使用Math.abs();
count++;
}
}
}
System.out.println(count);
}
}
本题重点:
- n<1000,为了方便,刚上手为了防止数组越界,可以适当设置大一点。也可以设置的精确一点
- 循环时,不能自己和自己比,所以第二个变量要从第一个变量 + 1+1 开始
- 各科成绩和总成绩比较的时候记得要取绝对值,否则比较数据有误
6.洛谷 p5769 Java 工艺品
AC代码:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
//输入这个工艺品的长宽高
int x = scanner.nextInt(), y = scanner.nextInt(), z = scanner.nextInt();
//输入切割的数据
int num = scanner.nextInt();
int[][][] a = new int[x + 1][y + 1][z + 1];//数据从1开始,所以在原来的基础上加一
for (int i = 0; i < num; i++) {//遍历输入每一次切割的数据
//输入起始点
int x1 = scanner.nextInt(), y1 = scanner.nextInt(), z1 = scanner.nextInt();
//输入终止点
int x2 = scanner.nextInt(), y2 = scanner.nextInt(), z2 = scanner.nextInt();
//使用标记法,将这一区域内的数据进行赋值标记
for (int j = x1; j <= x2; j++) {
for (int k = y1; k <= y2; k++) {
for (int l = z1; l <= z2; l++) {
a[j][k][l] = 1;
}
}
}
}
int counter = 0;//剩下的工艺品还剩下的体积,因为是1*1*1的所以直接累加即可
//遍历整个工艺品,没有被标记赋值的部分累加起来,
for (int i = 1; i <= x; i++) {
for (int j = 1; j <= y; j++) {
for (int k = 1; k <= z; k++) {
if (a[i][j][k] == 0) {
counter++;
}
}
}
}
System.out.println(counter);
}
}
本题重点:
- 使用标记法,将切割的部分进行赋值标记。
7.洛谷 p2550 Java 彩票摇奖
AC代码:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[][] a = new int[n][7];//自己买的我彩票
int[] b = new int[7]; //中奖的彩票
int[] c = new int[]{0,0,0,0,0,0,0}; //中奖个数数组
//给中奖的彩票赋值
for (int i = 0; i < 7; i++) {
b[i] = sc.nextInt();
}
//给自己彩票赋值
for (int i = 0; i < n; i++) {
for (int j = 0; j < 7; j++) {
a[i][j] = sc.nextInt();
}
}
for (int i = 0; i < n; i++) {//这个循环是自己的每张彩票
int temp = 0;
for (int j = 0; j < 7; j++) {//遍历一张彩票的每个数据
for (int k = 0; k < 7; k++) {//遍历中奖彩票的每一个数据
if (b[k] == a[i][j]){
temp++;
}
}
}
if (temp != 0){
c[7 - temp ]++;//因为自己的彩票上与中奖彩票相同的越多则计数越大,将屏数越小。
}
}
for (int i = 0; i < 7; i++) {//遍历中奖数组,一次输出各种将中奖的次数
System.out.print(c[i] + " ");
}
}
}
本题重点:
- 自己彩票和中奖彩票的对比,嵌套遍历。
- 要注意,自己的彩票和中奖彩票右越多的一样的而中奖的数越小(六等奖。五等奖,,,)
8.洛谷 p1554 Java 梦中的统计
AC代码:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int m = sc.nextInt();
int n = sc.nextInt();
int[] a = new int[10];
int count;
for (int i = m; i <= n; i++) {
int j = i;//避免循环致使i值出现混乱,将i赋值给新的值j
while (j != 0){//对每一个数进行操作:将每一个数 模10求余个位数,然后除以十得到倒数第二位以此类推将数据的每一位都求出来对1 - 10进行比较,相同则数组对应的数据自增;
count = j % 10;
if(count == 0)a[0]++;
if(count == 1)a[1]++;
if(count == 2)a[2]++;
if(count == 3)a[3]++;
if(count == 4)a[4]++;
if(count == 5)a[5]++;
if(count == 6)a[6]++;
if(count == 7)a[7]++;
if(count == 8)a[8]++;
if(count == 9)a[9]++;
j = j / 10;//让数据的每一位都可以是个位,然后求余得到各个位上的数
}
}
//遍历得到区段类数据中包含0 - 9中各个数有多少个。
for (int i = 0; i < 10; i++) {
System.out.print(a[i] +" ");
}
}
}
本题重点:
- 通过取余,除10的操作来获得一个数据各个位上的数,在对其进行判断,在对应的数组中进行累加。
- 最外层i遍历的时候,要将i值重新赋值给其他变量,防止后面的数据改变扰乱原有的数据。
9.洛谷 p1614 Java 爱与愁的心痛
AC代码:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();//有n件不爽的事情
int m = sc.nextInt();//连续m件
int[] a = new int[n];
int sum = 0,count = 0;
for (int i = 0; i < n; i++) {
a[i] = sc.nextInt();//给n件不爽的事情进行刺痛点赋值
}
for (int i = 0; i < m; i++) {
count += a[i];//初始化一个最初的m件不爽的事情的累加
}
for (int i = 0; i < n - m; i++) {//遍历从0开始到n - m;因为内层循环是i开始的m个值,防止数组越界
for (int j = i; j <i + m; j++) {//遍历每一个i值及后面m个值的累加值
sum += a[j];
}
if(sum < count){//判断获得最大值
count = sum;
}
sum = 0;//每一次累加之后进行重置,方便下一次使用
}
System.out.println(count);
}
}
本题重点:
- 注意遍历的起始点和结束点的选取,防止数组越界。
- 注意几个中间变量的使用。
10洛谷 p2911 Java Bovine Bones G
AC代码:
import java.util.Arrays;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int s1 = sc.nextInt();
int s2 = sc.nextInt();
int s3 = sc.nextInt();
int[] a = new int[16000];//定义一个数组,将对应的面和放在作为数组中对应的位置上
int maxx = 0,maxs = 0;
//遍历三个面,让面和对应的数组的值自增。
for (int i = 1; i <= s1 ; i++) {
for (int j = 1; j <= s2 ; j++) {
for (int k = 1; k <= s3 ; k++) {
a[i + j + k]++;
}
}
}
//因为面和的值是连续的从三个面最小值到三和面和的最大值
for (int i = s1 + s2 + s3; i >= 3 ; i--) {
if (a[i] >= maxx){//看面和对应的数组中的数据谁最大
maxx = a[i];
// maxs = i;
}
}
System.out.println(maxs);
}
}
本题重点:
- 我们可以定义一个数组专门的存放各个面累加出现的结果,然后遍历这三组数据,当单个面的数据与莫一结果相同时就在数中累加一;
- 定义一个大数组,防止越界,在里面直接在数组对应的面和的地方进行自增,这个比较的巧妙。
11.洛谷 p1161 Java 开灯
AC代码:
第一种:使用异或思想解决本题
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt(),t,result = 0;
double a;
for (int i = 0; i < n; i++) {//遍历输入每一组数据,
a = sc.nextDouble();
t = sc.nextInt();
for (int j = 1; j <= t; j++) {//
int x = (int)Math.floor(a*j);//[k]表示实数kk的整数部分,每一次操作都有t盏灯进行开或者关。
result ^= x;//使用异或,开关灯是对应的,n次操作后只有一盏灯是亮的,所以使用异或可以的到最后那个亮的
}
}
System.out.println(result);
}
}
第二种:使用数组解决问题
import java.util.Scanner;
public class Main {
public static void main(String args[]) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
double a[] = new double[n];
int t[] = new int[n];
// 输入
for(int i = 0;i<n;i++) {
a[i] = scanner.nextDouble();
t[i] = scanner.nextInt();
}
int x[] = new int[2000000];//对于%100%的数据,满足T≤2000000
// 设置开关的状态
for(int i = 0;i<x.length;i++) {
x[i] = 0;//起始的时候都是关闭状态
}
// 开关状态的改变
for(int i = 0;i<n;i++) {
for(int j = 1;j<=t[i];j++) {
if(x[(int) (a[i]*j)] == 1) {
x[(int) (a[i]*j)] = 0;
}else {
x[(int) (a[i]*j)] = 1;
}
}
}
// 输出结果
for(int i = 0;i<x.length;i++) {
if(x[i] == 1) {
System.out.println(i);
}
}
}
}
本题重点:
- 可以使用异或思想解决问题,这个可能刚入门的童鞋不是很清楚,可以网上查阅了解一下,注意异或的部分是二进制各位上的异或。
- 学习数组阶段还是使用第二种的方案,标记法,将开灯赋值为1;关闭的等赋值为0;最后遍历为1的灯即可。
12.洛谷 p5731 Java 蛇形方阵
AC代码:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[][] a = new int[n][n];
int count = 1;
int x = 0,y = 0;
a[0][0] = 1;
while(count < n * n){//输出边长为n的数字矩阵,所以最后的count值为n*n,以此作为判断。
//向右递增
while (y + 1 < n && a[x][y + 1] == 0){//向右下一个数据存在且值为0,表示未被赋值
a[x][++y] = ++count;
}
//向下递增
while (x + 1 < n && a[x + 1][y] == 0){//同上
a[++x][y] = ++count;
}
//向左递增
while (y - 1 >= 0 && a[x][y - 1] == 0){//同上
a[x][--y] = ++count;
}
//向上递增
while (x - 1 >= 0 && a[x - 1][y] == 0){//同上
a[--x][y] = ++count;
}
}
//遍历获得正方型矩阵
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
System.out.printf("%3d",a[i][j]);//使用printf来让每个数字占3个字符。
}
System.out.println();
}
}
}
本题重点:
- 循环+四个方向的正确判断,注意:别数组越界。
- 输出的时候每个数字占3个字符。
13.洛谷 p5732 Java 杨辉三角
AC代码:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[][] a = new int[n + 1][n + 1];//数组的数据从1开始,所以数组范围+1;
//将每一行的第一个和最后一个赋值为1;数组从1开始,方便
for (int i = 1; i <= n; i++) {
a[i][1] = a[i][i]=1;
}
//头两行在给每行的第一个和最后一个复制的同时就复制了,所以从第三行开始即可。
if (n > 2){
for(int i = 3;i <= n;i++){
for(int j = 2;j <= i;j++)//因为a[i][1]、a[i][i]已经赋值过了,所以循环是2~n-1
a[i][j] = a[i-1][j] + a[i-1][j-1];
}
}
//输出每一行的数据;
for (int i = 1; i <= n ; i++) {
for (int j = 1; j <= i; j++) {
System.out.print(a[i][j] + " ");
}
System.out.println();//内部循环遍历完成之后进行换行
}
}
}
本题重点:
- 每行数据的第一个和最后一个是1,
- 从第三行开始遍历的时候,注意他们之间的关系,注意数组遍历的起始位置和结束位置。
14.洛谷 p1789 Java 插火把 (较难理解)
AC代码:
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();//m个火把
int k = sc.nextInt();//k个萤石
int[][] arr = new int[n + 3][n + 3];//数组的范围定义大一点,下面遍历就不用考虑数组越界了 。
if (n == 2) {//可能没有萤石,但一定有火把。所以当n <= 2 的时候,没有怪物。
System.out.print("0");
} else {
//遍历火把的情况,将火把范围内的点赋值1
for (int i = 1; i <= m; i++) {
int x = sc.nextInt();
int y = sc.nextInt();
x = x + 2;
y = y + 2;
for (int j = x - 2; j < x + 1; j++) {
for (int l = y - 2; l < y + 1; l++) {
arr[j][l] = 1;
arr[x - 3][y - 1] = 1;
arr[x + 1][y - 1] = 1;
arr[x - 1][y - 3] = 1;
arr[x - 1][y + 1] = 1;
}
}
}
//遍历萤石的情况,将萤石范围内的点赋值1
for (int i = 1; i <= k; i++) {
int x = sc.nextInt();
int y = sc.nextInt();
x = x + 2;
y = y + 2;
for (int j = x - 3; j <= x + 1; j++) {
for (int l = y - 3; l <= y + 1; l++) {
arr[j][l] = 1;
}
}
}
int count = 0;//计数变量
//遍历整个数组,若数组内元素的值为0则是火把和萤石未照耀的部分将出现怪物
for (int i = 2; i < n + 2; i++) {
for (int j = 2; j < n + 2; j++) {
if (arr[i][j] == 0) {
count++;
}
}
}
System.out.print(count);
}
}
}
本题重点:
-
使用标记法,定义数组之后,数组内部元素会初始全部赋值为0,让火把和萤石照耀的部分赋值为1,最后遍历矩阵,数组元素为0的部分是未照耀的部分,会出现妖怪。
-
将数组的范围定义的大一点,不用考虑数组是否越界等问题,担当我们遍历的时候只用将自己想用的那部分遍历出来即可,
15.洛谷 p1319 Java 压缩技术
AC代码:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();//输入 n;
int middle = 0,b = 0, c, d = 0;//定义几个变量,
while(d < n * n){
c = sc.nextInt();
middle++;//middle变量的作用是交换输出0和1
for (int j = c; j > 0 ; j--) {
if (b == n){//当每一行达到n的时候换行且将变量重新赋值为0
System.out.println();
b = 0;
}
//交替输出0和1
if (middle % 2 == 1){//排序奇数时
System.out.print("0");
}else{//偶数时
System.out.print("1");
}
b++;//控制每一行输出数字的个数
d++;//控制while循环
}
}
System.out.println();
}
}
本题重点:
- 如何控制在排序为奇数时输出相对应的0,在偶数时输出对应的1,本题中间变量的使用较多,做题时捋清楚头绪。
- 控制换行
16.洛谷 p2615 Java 神奇的幻方
AC代码:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[][] a = new int[n][n];
int x = 0,y = n / 2;
a[0][y] = 1;
for (int i = 2; i <= n*n; i++) {
if (x == 0 && y != n-1) {
x = n-1;
++y;
} else if (x != 0 && y == n-1) {
--x;
y = 0;
} else if (x == 0 && y == n-1) {
++x;
} else if (a[x-1][y+1] == 0) {
--x;
++y;
} else {
++x;
}
a[x][y] = i;
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
System.out.print(a[i][j] + " ");
}
System.out.println();
}
}
}
本题重点:
-
注意题目说的是,幻方的N必定是奇数(实测确实不能是偶数),那就不需要检测了。
-
按指定的要求移动就行。并不需要自己设计什么算法。
17.洛谷 p2141 Java 珠心算测试
AC代码:
import java.util.*;
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[] m = new int[n];
for(int i = 0; i < n; i++){
arr[i] = sc.nextInt();
m[i] = 1;//将数组m的所有函数赋值为1做标记
}
int count = 0;
for(int i = 0; i < n; i++ ){
for(int j = i + 1; j < n; j++){
for(int k = 0; k < n; k++){
if(arr[i] + arr[j] == arr[k] && m[k] == 1){//判断数据是否符合条件
count++;
m[k] = 0;//若符合条条件的那个数将数组中对应的位置赋值为0,防止下次再进来计算,
}
}
}
}
System.out.println(count);
}
}
本题重点:
- 使用标记法,得到恰好等于集合中另外两个(不同的)数之和后将此数标记,防止下次再次计算上,如:1+4=5;2+3=5;若5没有被标记,则5将会被累加两次。