索引
打印图形
面试题 01.07. 旋转矩阵(降低空间复杂度)
class Solution {
public void rotate(int[][] matrix) {
int a=0;
int b=0;
int c=matrix.length-1;
int d=matrix[0].length-1;
while(a<c){
process(matrix,a++,b++,c--,d--);
}
}
public void process(int[][] arr,int a,int b,int c,int d){
int len = d-b;
int temp=0;
for(int i=0;i<len;i++){
temp=arr[a][b+i];
arr[a][b+i] = arr[c-i][b] ;
arr[c-i][b] = arr[c][d-i];
arr[c][d-i] = arr[a+i][d];
arr[a+i][d] = temp;
}
}
}
54. 螺旋矩阵(宏观上看 对角线夹逼法)
package class03;
import java.util.ArrayList;
import java.util.List;
class Solution {
public static List<Integer> list = new ArrayList<>();
public static List<Integer> spiralOrder(int[][] matrix) {
//维护左上角和右下角的四个位置指针
int a=0,b=0;
int c=matrix.length-1,d=matrix[0].length-1;
//打印一圈后向内缩
while(a<=c&&b<=d){
print(matrix,a++,b++,c--,d--);
}
return list;
}
//打印一圈
public static void print(int[][] matrix, int a, int b, int c, int d){
if(a==c){
for(int i=b;i<=d;i++){
list.add(matrix[a][i]);
}
}else if(b==d){
for(int i=a;i<=c;i++){
list.add(matrix[i][b]);
}
}else{
int curB = b;
int curA = a;
while (curB != d) {
list.add(matrix[a][curB]);
curB++;
}
while (curA != c) {
list.add(matrix[curA][d]);
curA++;
}
while (curB != b) {
list.add(matrix[c][curB]);
curB--;
}
while (curA != a) {
list.add(matrix[curA][b]);
curA--;
}
}
}
public static void main(String[] args) {
int[][] arr = new int[][]{{1,2,3,4}
,{5,6,7,8}
,{9,10,11,12}};
System.out.print(spiralOrder(arr));
}
}
蛇形矩阵
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int l=1;
int[][] arr = new int[100][100];
for(int i=1;i<=n;i++){
for(int j=0;j<i;j++){
arr[i-1-j][j]=l;
l++;
}
}
int h=0;
for(int i=0;i<n;i++,h++){
for(int j=0;j<n-h;j++){
System.out.print(arr[i][j]+" ");
if(i+j==n-1){
System.out.println();
}
}
}
}
}
枚举
分糖果
public class Main {
static int res=0;
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int[] m = new int[n];
for(int i=0;i<n;i++){
m[i] = scanner.nextInt();
}
//维护一个数组用来存放分了的数
int[] num = new int[n];
process(m);
//判断数组是否糖果数相同,如有,返回奇数个数并奇数变偶数
while(isj(m)){
process(m);
}
System.out.println(res);
}
public static boolean isj(int[] m){
int num =0;
for(int i=0;i<m.length;i++){
if(m[i]%2!=0){
res++;
m[i]+=1;
}
}
for(int i=0;i<m.length-1;i++){
if(m[i]!=m[i+1]){
num++;
}
}
if(num!=0){
return true;
}else{
return false;
}
}
public static int[] process(int[] m){
int[] num = new int[m.length];
for(int i=0;i<m.length;i++){
m[i]=m[i]/2;
num[i]=m[i];
}
for(int i=1;i<m.length;i++){
m[i]=m[i]+num[i-1];
}
m[0] = m[0]+num[num.length-1];
return m;
}
}
矩阵相乘
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int m = scanner.nextInt();
int[][] arr = new int[n][n];
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
arr[i][j]= scanner.nextInt();
}
}
if(m==0){
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(i==j){
System.out.print(1+" ");
}else{
System.out.print(0+" ");
}
}
System.out.println();
}
return;
}
int[][] res = arr;
for(int i=1;i<m;i++){
res = process(arr,res);
}
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
System.out.print(res[i][j]+" ");
}
System.out.println();
}
}
//将arr 与 temp 相乘 结果保存为 arr
public static int[][] process(int[][] arr,int[][] temp){
int[][] res = new int[arr.length][arr.length];
for(int i=0;i< arr.length;i++){
for(int j=0;j< arr.length;j++){
for(int p=0;p< arr.length;p++){
res[i][j]+= arr[i][p]*temp[p][j];
}
}
}
return res;
}
}
说谎话
public class Main {
public static void main(String[] args) {
int[] arr = new int[6];
for(int i=0;i<arr.length;i++){
arr[i] = 1;
int res = 0;
boolean A = false,B = false,C = false,D = false,E = false,F = false;
if(arr[0]!=1){
A = true;
res++;
}
if(arr[0]==1||arr[2]==1){
B = true;
res++;
}
if(!A&&!B){
C = true;
res++;
}
if(arr[5]==1){
F = true;
res++;
}
if(!C&&!F){
D = true;
res++;
}
if(A&&!B&&!C&&D&&!F){
E = true;
res++;
}
if(res==3){
System.out.println((char)(i+'A'));
}
arr[i] = 0;
}
}
}
2672 蓝桥杯2022年第十三届省赛真题-字符统计
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
String str=sc.next();
char[] arr = str.toCharArray();
//sort(arr);
int[] list = new int[26];
for(int i=0;i<arr.length;i++){
int j = arr[i]-'A';
list[j]++;
}
int min = -1 , r =-1;
for(int i =0 ; i< list.length; i++){
if(list[i]>min){
r = i;
min = list[i];
}
}
int[] res = new int[list.length];
for(int i =0 ; i< list.length; i++){
if(list[i]==list[r]){
res[i] = i+1;
}
}
for(int i : res){
if(i!=0){
System.out.print((char)('A'+i-1));
}
}
}
}
子矩阵问题
这类问题往往复杂度比较高,可以注意一下数据范围,将某一维度(数据量小)的数据压缩,使整体枚举数据量小的维度,另一数据量大的维度可以采用二分等策略简化复杂度,,大部分情况下都会使用dp来存储处理后的数据,以简化复杂度
最大子矩阵(求差)
2061: [蓝桥杯2022初赛] 最大子矩阵
题解
前缀和问题
前缀和可以简单理解为「数列的前 n项的和」,是一种重要的预处理方式,能大大降低查询的时间复杂度
前缀和有时会和哈希表一起使用,用来解决例如连续子数组之类的问题。
二维前缀和
具体实施步骤
1 .预处理二维数组
S(i , j) = S(i - 1 , j) + S(i , j - 1)- S( i - 1 , j - 1) +map[ i ][j]
2.实践求某个矩形/正方形区域内的数字和
核心思想 : 还是拼凑出我们想求的区域的面积(这里的面积就是我们所求区域的数字和),通过之前的 预处理操作
S = S(x1 - 1 + a , y1 - 1 + b) - S(x1 - 1 + a , y1- 1) - S(x1 - 1 , y1 - 1 + b) + S(x1 - 1 , y1 - 1 )
2036: [蓝桥杯2022初赛] 统计子矩阵 (枚举题型的优化解)
链接
未ac 只过了80%
package test;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int a = in.nextInt();
int b = in.nextInt();
int k = in.nextInt();
long[][] arr = new long[a+1][b+1];
for(int i = 1;i<=a;i++){
for(int j = 1;j<=b;j++) {
arr[i][j] = arr[i-1][j] +arr[i][j-1] - arr[i-1][j-1] + in.nextInt();
}
}
int res=0;
for(int i = 1;i<=a;i++){
for(int j = 1;j<=b;j++) {
for(int m = i;m<arr.length;m++){
for(int n = j;n<arr[0].length;n++) {
if( arr[m][n] - arr[m][j-1] - arr[i-1][n] + arr[i-1][j-1] <=k){
res++;
}else{
break;
}
}
}
}
}
System.out.println(res);
}
}
逆序对问题
D 蓝桥杯历届试题-小朋友排队
求逆序对的问题
解法:
- 树形数组
- 归并排序
归并排序模板:
class Solution {
static int count=0;
public static void main(String[] args) {
int[] arr = new int[]{4,5,6,7};
System.out.println(reversePairs(arr));
}
public static int reversePairs(int[] nums) {
sort(nums,0,nums.length-1);
return count;
}
public static void sort(int[] nums,int l,int r){
if(l>=r){
return;
}
int med = l+(r-l)/2;//(r+l)/2 是否可能越界
sort(nums,l,med);
sort(nums,med+1,r);
if(nums[med]>nums[med+1]){
merge(nums,l,med,r);
}
}
public static void merge(int[] nums,int l,int med,int r){
int[] temp = new int[nums.length];
System.arraycopy(nums,l,temp,l,r-l+1);
int i=l,j=med+1;
for(int k=l;k <= r;k++){//temp是旧值 arr是新值跟随移动
if(i>med){
nums[k] = temp[j++];
}
else if(j>r){
nums[k]=temp[i++];
}
else if(temp[i]<=temp[j]){
nums[k]=temp[i++];
}
else if(temp[i]>temp[j]){
nums[k]=temp[j++];
count += med-i+1;
}
}
}
}
import java.util.Scanner;
public class Main {
public static person[] Child;
public static person[] temp;
static class person{
public int high;
public int count;
public person(int high){
this.high=high;
}
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int num = scanner.nextInt();//人数
Child = new person[num];
temp = new person[num];
for(int i=0;i<num;i++){
int a = scanner.nextInt();
Child[i] = new person(a);
}
sort(0,num-1);
long res = 0;
for (int i = 0; i < num; i++) res += cal(Child[i].count);
System.out.println(res);
}
private static long cal(int k) {
return ((long) k * (k + 1)) / 2;
}
public static void sort(int l,int r){
if(l>=r) return;
int med = l+(r-l)/2;
sort(l,med);
sort(med+1,r);
merge(l,med,r);
}
public static void merge(int l,int med,int r){
int i = l,j=med+1 , k=l;
while(i<=med&&j<=r){
if(Child[i].high>Child[j].high){
Child[j].count += med-i+1;
temp[k++] = Child[j++];
}else{
temp[k++] = Child[j++];
}
}
while(i<=med&&j>r){
Child[i].count += r-med;
temp[k++] = Child[i++];
}
while(i>med&&j<=r){
temp[k++] = Child[j++];
}
for (int m = l; m <= r ; m++) {
Child[m] = temp[m];
}
}
}
dfs思想
C K-进制数
dfs思想
import java.util.Scanner;
/*
考虑包含N位数字的K-进制数. 定义一个数有效, 如果其K-进制表示不包含两连续的0.
*/
public class C_1102 {
static int n;
static int k;
static int count=0;
static int[] temp = new int[20];
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
k = sc.nextInt();
dfs(0);
System.out.println(count);
}
public static void dfs(int bit){//当前指向的位
//baseCase
if(bit==n){
count++;
return;
}
//对于0——k-1位搜索
for(int i=0;i<k;i++){
if((bit==0&&i==0)||(i==0&&temp[bit-1]==0)){
continue;
}
temp[bit]=i;
dfs(bit+1);
}
}
}
技巧题
517. 超级洗衣机(贪心)(维持一个累加和)
对每个当前单位而言 左边最少的支出或盈余 右边最少的支出或盈余
class Solution {
public static int findMinMoves(int[] machines) {
int nums =0;
for(int i : machines){
nums+=i;
}
if(nums%machines.length!=0){
return -1;
}
int n = nums/machines.length;
int res = 0;
int leftSum =0;
for(int i=0;i<machines.length;i++){
int leftRest = leftSum-i*n;
int rightRest = (nums-machines[i]-leftSum) - n*(machines.length - i-1) ;
if(leftRest<0&&rightRest<0){
res = Math.max(res,Math.abs(leftRest)+Math.abs(rightRest));
}else{
res = Math.max(res,Math.max(leftRest,rightRest));
}
leftSum += machines[i];
}
return res;
}
}