一、修改数组(中等)
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=Cn−1m+Cn−1m−1
然后再求关于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