【算法练习】蓝桥杯C++ AB组辅导课题单:第十讲(Java解答版)

一、修改数组(中等)

在这里插入图片描述

import java.util.*;
import java.io.*;

public class Main {
    static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    static BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));
    static int[] parent = new int[1000005];
    public static void main(String[] args) throws IOException {
    	String[] input = reader.readLine().trim().split(" ");
    	int n = Integer.parseInt(input[0]);
    	input = reader.readLine().trim().split(" ");
    	// 初始化parent数组
    	for (int i = 0; i < 1000005; i++) {
    		parent[i] = i;  // 只想自己
    	}
    	for (int i = 0; i < n; i++) {
    		int num = Integer.parseInt(input[i]);
    		int root= find(num);  // 第一个数肯定指向自己,可以直接打印
    		System.out.print(root + " ");
    		parent[root] = root + 1;  // 让当前被放过的数的根节点变成root + 1,也就是比以前的数大一
    		// 这样下一次如果和之前数一样的话,找到根节点也就是root + 1,就和上一次的结果不同了!
    	}
	}
    // 查根节点
    static int find(int x) {
    	// 用路径压缩找根节点
    	while (x != parent[x]) {
    		parent[x] = parent[parent[x]];
    		x = parent[x];
    	}
    	return x;
    }
}

二、倍数问题(中等)

在这里插入图片描述
在之前的这篇文章已经进行了讲解(主要是取模的运算问题)

import java.util.*;
import java.io.*;

public class Main {
    static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    static BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));
    public static void main(String[] args) throws IOException {
    	String[] input = reader.readLine().trim().split(" ");
    	int n = Integer.parseInt(input[0]);
    	int k = Integer.parseInt(input[1]);
    	input = reader.readLine().trim().split(" ");
    	// 只记录对数取k的余数的前三大结果
    	int[][] top = new int[k][3];
    	for (int i = 0; i < n; i++) {
    		int t = Integer.parseInt(input[i]);
    		int r = t % k;
    		if (t > top[r][0]) {
    			// 它现在是最大的
    			top[r][2] = top[r][1];
    			top[r][1] = top[r][0];
    			top[r][0] = t;
    		} else if (t > top[r][1]) {
    			// 第二大的
    			top[r][2] = top[r][1];
    			top[r][1] = t;
    		} else if (t > top[r][2]) {
    			top[r][2] = t;
    		}
    	}
    	// 遍历前两个数的余数,就可以确定第三个数的余数
    	// c % k = (k - (a % k + b % k) % k) % k
    	long sum = 0;
    	for (int i = 0; i < k; i++) {
    		for (int j = 0; j < k; j++) {
    			int t = (k - (i + j) % k) % k;
    			long tmp = 0;
    			if (i == j) {
    				// i j余数相同
    				tmp += top[i][0];
    				tmp += top[i][1];
    				if (t == i) {
    					tmp += top[i][2];
    				} else {
    					tmp += top[t][0];
    				}
    			} else {
    				tmp += top[i][0];
    				tmp += top[j][0];
    				if (t == i || t == j) {
    					tmp += top[t][1];
    				} else {
    					tmp += top[t][0];
    				}
    			}
    			if (tmp > sum) sum = tmp;
    		}
    	}
    	System.out.println(sum);
    }
}

三、斐波那契(中等)

在这里插入图片描述
用矩阵快速幂(还不会)

四、距离(中等)

在这里插入图片描述

五、剪格子(中等)

在这里插入图片描述

在这里插入图片描述
题目中说了必须包含左上角的点。

import java.util.*;
import java.io.*;

public class Main {
    static int[][] nums;
    static boolean[][] vis;
    static int[] x = new int[] {-1,1,0,0};
    static int[] y = new int[] {0,0,-1,1};
    static int ans = Integer.MAX_VALUE;
    static int sum;
    static int n, m;
    public static void main(String[] args) throws IOException, Exception {
    	Scanner scan = new Scanner(System.in);
    	m = scan.nextInt();
    	n = scan.nextInt();
    	nums = new int[n][m];
    	vis = new boolean[n][m];
    	sum = 0;
    	for (int i = 0; i < n; i++) {
    		for (int j = 0; j < m; j++) {
    			nums[i][j] = scan.nextInt();
    			sum += nums[i][j];
    		}
    	}
    	// 一半
    	sum /= 2;
    	vis[0][0] = true;
    	dfs(0, 0, 1, nums[0][0]);
    	System.out.println(ans);
    }
    static void dfs(int i, int j, int cnt, int total) {
    	if (cnt > ans) return;
    	if (total == sum) {
    		ans = Math.min(ans, cnt);
    		return;
    	}
    	for (int k = 0; k < 4; k++) {
    		int tx = i + x[k];
    		int ty = j + y[k];
    		if (tx < 0 || ty < 0 || tx >= n || ty >= m || vis[tx][ty]) continue;
    		vis[tx][ty] = true;
    		dfs(tx, ty, cnt + 1, total + nums[tx][ty]);
    		// 回溯
    		vis[tx][ty] = false;
    	}
    }
}

下面贴一道更加困难的,类似题目

六、剪邮票(中等)

剪邮票

如【图1.jpg】, 有12张连在一起的12生肖的邮票。
现在你要从中剪下5张来,要求必须是连着的。
(仅仅连接一个角不算相连)
比如,【图2.jpg】,【图3.jpg】中,粉红色所示部分就是合格的剪取。

请你计算,一共有多少种不同的剪取方法。

请填写表示方案数目的整数。
注意:你提交的应该是一个整数,不要填写任何多余的内容或说明性文字。

在这里插入图片描述
这道题的详细解答过程已经在之前的博客中做出了讲解:蓝桥杯-剪邮票
主要是要考虑一个12选5的全排列问题 + DFS检测连通性

import java.util.*;
import java.io.*;

public class Main {
	static int[] nums = new int[] {0,0,0,0,0,0,0,1,1,1,1,1};  // 12选5
	static LinkedList<Integer> tmp = new LinkedList<>();
	static boolean[] vis = new boolean[13];
	static int ans = 0;
	static int[] x = new int[] {-1,1,0,0};
	static int[] y = new int[] {0,0,-1,1};
	public static void main(String[] args) throws IOException, Exception {
    	dfs();
    	System.out.println(ans);
    }
    // 连通性检测
	static void dfss(int[][] g, int i, int j) {
		for (int k = 0; k < 4; k ++) {
			int tx = i + x[k];
			int ty = j + y[k];
			if (tx < 0 || ty < 0 || tx >= 3 || ty >= 4 || g[tx][ty] == 0) continue;
			g[tx][ty] = 0;
			dfss(g, tx, ty);
		}
	}
	static void dfs() {
		if (tmp.size() == 12) {
			// 12个数都拿到了
			int[][] g = new int[3][4];
			for (int i = 0; i < 3; i++) {
				for (int j = 0; j < 4; j++) {
					if (tmp.get(i * 4 + j) == 1) {
						g[i][j] = 1;
					} else {
						g[i][j] = 0;
					}
				}
			}
			// 连通性检测
			int cnt = 0;
			for (int i = 0; i < 3; i++) {
				for (int j = 0; j < 4; j++) {
					if (g[i][j] == 1) {
						g[i][j] = 0;
						cnt++;
						dfss(g, i, j);
					}
				}
			}
			// 连通
			if (cnt == 1) ans++;
			return;
		}
		for (int i = 0; i < 12; i++) {
			// 全排列去重,一定要注意:vis[i - 1] == false
			if (i > 0 && nums[i] == nums[i - 1] && vis[i - 1] == false) continue;
			if (vis[i]) continue;
			vis[i] = true;
			tmp.add(nums[i]);
			dfs();
			// 回溯
			vis[i] = false;
			tmp.removeLast();
		}
	}
}

※七、组合数问题(简单)

在这里插入图片描述
首先需要知道的是组合数的递推公式:
C n m = C n − 1 m + C n − 1 m − 1 C_n^m = C^m_{n-1} + C^{m - 1}_{n - 1} Cnm=Cn1m+Cn1m1
然后再求关于m、n的二维前缀和,这个前缀和的意义是:C(i,j)是k的倍数的个数。

在这里插入图片描述

import java.util.*;
import java.io.*;

public class Main {
	static BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));
	static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
	public static void main(String[] args) throws IOException {
		String[] input = reader.readLine().trim().split(" ");
		int t = Integer.parseInt(input[0]);
		int k = Integer.parseInt(input[1]);
		// C(n,m) = C(n - 1, m) + C(n - 1, m - 1),先预处理C(n, m)模k的值
		int[][] c = new int[2010][2010];
		// 前缀和数组:表示C(i,j) 0 <= i <= n, 0 <= j <= min(i,m)中k的倍数的个数
		int[][] s = new int[2010][2010];
		for (int i = 0; i < 2010; i++) {  // n
			for (int j = 0; j <= i; j++) {  // m
				//C(n,m):m <= n C(0,0) = 1 C(1,0) = 1 ... C(n,0) = 1
				if (j == 0) c[i][j] = 1 % k;
				else c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % k;
				if (c[i][j] == 0) {
					// 说明是k的倍数,这只是初始化数组,之后再计算二维前缀和
					s[i][j] = 1;
				}
			}
		}
		// 构造关于k倍数的个数的前缀和(二维)
		for (int i = 0; i < 2010; i++) {
			for (int j = 0; j < 2010; j++) {
				if (i != 0) s[i][j] += s[i - 1][j];
				if (j != 0) s[i][j] += s[i][j - 1];
				if (i != 0 && j != 0) s[i][j] -= s[i - 1][j - 1];  // 只有两个减时,才减,三维同理
			}
		}
		while (t-- > 0) {
			input = reader.readLine().trim().split(" ");
			int n = Integer.parseInt(input[0]);
			int m = Integer.parseInt(input[1]);
			writer.write(s[n][m] + "\n");
		}
		writer.flush();
	}
}

八、模拟散列表

https://blog.csdn.net/Rat1onal/article/details/104190673

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

@u@

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

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

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

打赏作者

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

抵扣说明:

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

余额充值