蓝桥杯31天冲刺之十 [java]

扫地机器人

题目链接:https://www.lanqiao.cn/problems/199/learning/

这个感觉有点难,放弃了

全球变暖

image-20220317145446447

题目链接:https://www.lanqiao.cn/problems/178/learning/

这个题有两个难点,一个是如何找到一个岛的全部陆地,一个是判断这个岛会不会被全部淹没

找到一个岛的全部陆地的话我们可以使用dfs或者bfs就可以找到属于这个岛的所有陆地面积,而判断一个岛会不会完全淹没就需要找在这个岛内有没有一片陆地周围没有海(如果三面是陆地,一面是墙也可以),这就是整个题的思路了,我这里使用的是bfs,然后在将一个节点的周围节点加入队列的时候会去当前这个周围的节点是不是海,如果是海,则直接将centerLand设置为false,如果四面都不是海,则将返回值设置为0,说明当前这个岛屿有一片地不是四面临海的,即不会被完全淹没

package daily;

import java.util.Deque;
import java.util.LinkedList;
import java.util.Scanner;

/**
 * https://www.lanqiao.cn/problems/178/learning/
 * 
 * @author Jia
 *
 */
public class day3_17_2 {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int N = sc.nextInt();
		char[][] land = new char[N][N];

		for (int i = 0; i < land.length; i++) {
			String str = sc.next();
			for (int j = 0; j < land.length; j++) {
				land[i][j] = str.charAt(j);
			}
		}
		sc.close();

		int ans = 0;
		boolean[][] visited = new boolean[N][N];// 记录这个结点是否已经看过
		for (int i = 0; i < N; i++) {
			for (int j = 0; j < N; j++) {
				if (visited[i][j] != true && land[i][j] == '#') {
					// 如果当前这块地是岛屿并且从来没有看过,则说明找到了一块新岛
					ans += bfs(i, j, land, visited);
				}
			}
		}
		System.out.println(ans);
	}

	/**
	 * 判断当前岛屿有没有不临海的地,并且把该岛屿的全部陆地面积都设置为已看过,防止重复遍历该岛屿
	 * 
	 * @param i
	 * @param j
	 * @param land
	 * @param visited
	 * @return 如果该岛屿有不临海的地则返回0,如果没有则返回1
	 */
	private static int bfs(int i, int j, char[][] land, boolean[][] visited) {
		int[][] direction = { { 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, 0 } };
		Deque<PointNode> queue = new LinkedList<>();// 创建一个队列
		queue.addFirst(new PointNode(i, j));
		int ret = 1;
		visited[i][j] = true;

		while (queue.size() != 0) {
			PointNode node = queue.removeLast();

			boolean centerLand = true;// 用来记录是不是周围没有海
			for (int k = 0; k < direction.length; k++) {
				int newCol = node.col + direction[k][0];
				int newRow = node.row + direction[k][1];
				if (newCol >= 0 && newCol < land.length && newRow >= 0 && newRow < land.length) {
					// 新的点要在整个的范围之内
					if (land[newRow][newCol] == '#' && visited[newRow][newCol] == false) {
						// 如果新的节点是陆地并且没有看过则加入队列中
						queue.add(new PointNode(newRow, newCol));
						visited[newRow][newCol] = true;
					}
					if (land[newRow][newCol] == '.') {
						// 如果周围有一个点是海,则说明这个点会被淹
						centerLand = false;
					}
				}
			}
			if (centerLand) {
				// 如果周围没有海,则说明一次淹不到,那么这座岛不会被完全淹没
				ret = 0;
			}
		}
		return ret;
	}

}

// 记录每个节点行列的类
class PointNode {
	int row;
	int col;

	public PointNode(int row, int col) {
		super();
		this.row = row;
		this.col = col;
	}
}

机器人行走

image-20220317150202750

题目链接:https://www.lanqiao.cn/problems/283/learning/

这个题就是纯模拟了,我觉得难的点就是方向不好去控制,我这里使用的还是经常使用的数组,用数组加一个下标标识目前的朝向,这样就可以避免了写四个if去判断当前的朝向以及复杂的转向控制,我这里在设置数组的元素时控制相邻元素直接左转右转就可以直接转到,方向顺序为右,下,左,上(建议参考xoy坐标系去想象,即四个方向的单位向量,方便后面与步数直接乘,计算移动后的位置),然后初始方向向右。在每一次转向时,如果为L,则faceTo=faceTo-1(当然要取余控制,具体看代码),如果为R,则faceTo=faceTo+1,这样就可以很容易的控制方向了

然后就是要走多少,首先区分一下当前的元素是数字还是字母,如果是数字的话就一直统计,直到出现字母的时候才清空步数,然后走的方向就是faceTo指向的方向位置,我这里直接使用单位向量乘步数,就可以得到朝哪个方向走了多少步

再就是输出保留两位小数,这里使用的是类似c的写法,使用了printf函数,其他方法可以参考Java输出结果保留两位小数_JacobGo的博客-CSDN博客_java输出保留两位小数,可以看看他里面的m2()方法,可以在计算的时候直接保留精度了

package daily;

import java.util.Scanner;

/**
 * https://www.lanqiao.cn/problems/283/learning/
 * 
 * @author Jia
 *
 */
public class day3_17_3 {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int N = sc.nextInt();

		// 四个方向分别是,右,下,左,上,保证左转右转可以衔接上
		int[][] direction = { { 1, 0 }, { 0, -1 }, { -1, 0 }, { 0, 1 } };

		for (int i = 0; i < N; i++) {
			String instruction = sc.next();
			// 执行指令
			executeInstruction(direction, instruction);
		}
		sc.close();
	}

	/**
	 * 执行指令
	 * 
	 * @param direction   方向
	 * @param instruction 指令
	 */
	private static void executeInstruction(int[][] direction, String instruction) {
		int j = 0;
		int num = 0;
		int x = 0;// x坐标
		int y = 0;// y坐标
		int faceTo = 0;// 当前的方向
		while (j < instruction.length()) {
			char ch = instruction.charAt(j);
			j++;
			if (Character.isDigit(ch)) {
				num = num * 10 + ch - '0';
				continue;
			}
			// 到这里说明就不是数字了,那么需要将积累的步数清空
			if (num != 0) {
				x += num * direction[faceTo][0];
				y += num * direction[faceTo][1];
				num = 0;
			}
			if (ch == 'L') {
				// 左转
				faceTo = (faceTo + 4 - 1) % 4;// 这里防止减出负数了
			} else {
				// 右转
				faceTo = (faceTo + 1) % 4;
			}
		}
		// 如果最后是以数字结尾的话需要在走出去一次
		if (num != 0) {
			x += num * direction[faceTo][0];
			y += num * direction[faceTo][1];
			num = 0;
		}

		// 计算长度并输出
		double length = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
		System.out.printf("%.2f\n", length);
	}
}

数的幂次

image-20220317153826425

题目链接:https://www.lanqiao.cn/problems/1181/learning/

这个题用到了快速幂的计算,用普通的计算指数幂的方法会超时,然后还用到了java的快读,用普通的Scanner会超时,这个也是个模板,记下就行了

首先有一个算式需要知道一下 ( a ∗ b ) % c = ( ( a % c ) ∗ ( b % c ) ) % c (a*b)\%c=((a\%c)*(b\%c))\%c (ab)%c=((a%c)(b%c))%c,也就是在计算乘法之前可以先取余,然后再进行乘法的计算,这样可以确保数字不会太大然后爆掉

至于快速幂,我自己是这么理解的,例如要计算 3 11 % 4 3^{11}\%4 311%4,那么就是计算 3 1011 % 4 = 3 1000 + 10 + 1 % 4 = ( 3 1000 % 4 ) ∗ ( 3 10 % 4 ) ∗ ( 3 1 % 4 )   % 4 3^{1011}\%4=3^{1000+10+1}\%4=(3^{1000}\%4) * (3^{10}\%4)*(3^{1}\%4) \ \%4 31011%4=31000+10+1%4=(31000%4)(310%4)(31%4) %4,那么这样之后就可以按位计算,首先 m = 1011 m=1011 m=1011,判断最低位是否为1,如果为1,则答案应该乘n并且对p取余,然后将m右移,将m右移就需要将n取平方,假设当前m挪了x位,这样对ans乘n就相当于乘了初始的n的 2 x 2^x 2x次方(这里可能有点绕,希望可以自己手算一下)

就拿上面那个例子来说,一步一步推一下(我这里这个例子不太好,最后n不变了,可以使用题目上给的用例试一下)

  • 初始时n=3,m=1011,p=4,ans=1,由于当前m的最低位为1,则ans=ans*n%p=3,然后m右移一位变成101,n取平方并模4变成1
  • 然后进入下一次循环,此时n=1,m=101,p=4,ans=3,由于m的最低位为1,则ans=ans*n%p=3,同时m右移,n取平方后再取模
  • 下一次循环,此时n=1,m=10,p=4,ans=3,由于m的最低位为0,因此无需计算ans,直接m右移,n取平方然后取模
  • 此时n=1,m=1,p=4,ans=3,由于m的最低位为1,因此ans=ans*n%p=3,同时m右移,n取平方后再取模
  • 因为此时m等于0,所以跳出循环

除此之外,还有一个需要注意一下,由于题目的N的范围是10的9次方,比int的范围少小一点,但是要计算n的平方,所以使用int必然会超出范围,所以使用的都是long去存储数据

package daily;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;

/**
 * https://www.lanqiao.cn/problems/1181/learning/
 * 
 * @author Jia
 *
 */
public class day3_17_4 {
	static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
	static StreamTokenizer in = new StreamTokenizer(reader);

	public static int nextInt() throws IOException {
		in.nextToken();
		return (int) in.nval;
	}

	public static void main(String[] args) throws IOException {
		int T = nextInt();
		for (int i = 0; i < T; i++) {
			int N = nextInt();
			int M = nextInt();
			int P = nextInt();
			compute(N, M, P);
		}
	}

	/**
	 * 快速幂计算
	 * 
	 * @param n
	 * @param m
	 * @param p
	 */
	private static void compute(long n, long m, long p) {
		long ans = 1;
		while (m > 0) {
			if ((m & 1) == 1) {
				ans = (ans * n) % p;
			}
			m = m >> 1;
			n = (n * n) % p;
		}
		System.out.println(ans);
	}
}
  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hydrion-Qlz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值