算法总结与归纳ForLiXinBo

printf()输出格式小tips

#include<stdio.h> 
#include<string.h> 
int main() 
{ 
	char c, s[20]; 
	int a=1234;
	float f=3.141592653589; 
	double x=0.12345678912345678; 
	strcpy(s, "Hello,World"); 
	c='\x41'; 
	printf("a=%d\n", a);//按照十进制整数格式输出,显示 a=1234
	printf("a=%d%%\n", a);//输出%号 结果 a=1234%
	printf("a=%6d\n", a);//输出6位十进制整数 左边补空格,显示 a= 1234
	printf("a=%06d\n", a);//输出6位十进制整数 左边补0,显示 a=001234
	printf("a=%2d\n", a);//a超过2位,按实际输出 a=1234
	printf("a=%-6d\n", a);///输出6位十进制整数 右边补空格,显示 a=1234
	printf("f=%f\n", f);//浮点数有效数字是7位,结果 f=3.141593
	printf("f=%6.4f\n", f);//输出6列,小数点后4位,结果 f=3.1416
	printf("x=%lf\n", x);//输出长浮点数 x=0.123457
	printf("x=%18.16lf\n", x);//输出18列,小数点后16位,x=0.1234567891234567
	printf("c=%c\n", c);     //输出字符 c=A
	printf("c=%x\n", c);//以十六进制输出字符的ASCII码 c=41
	printf("s[]=%s\n", s);//输出数组字符串s[]=Hello,World
	printf("s[]=%6.9s\n", s);//输出最多9个字符的字符串 s[]=Hello,Wor
	return 0;
}

排序


冒泡排序

int a[];
for(int i=0;i<a.length-1;i++)
{   //一轮过后已经选出最大的一个并排好,所以要-i
    for(int j=0;j<a.length-1-i;j++){
        if(a[j]>a[j+1])//从小到大排序
        {
            int temp=a[j];
            a[j]=a[j+1];
            a[j+1]=temp;
        }
    }
}
//最坏情况下,时间复杂度为O{n(n-1)/2}

*快排

基于分治

1.确定分界点

2.调整区间

3.递归

//快排 2.0
public static void quicksort(int[] arr, int left, int right) {
		if (left > right) {
			return;
		}
		int base = arr[(left + right) / 2];//确定分界点
		int i = left;
		int j = right;
		while (i <= j) {
			while (arr[j] > base) {
				j--;
			}
			while (arr[i] < base) {
				i++;
			}
			if (i <= j) {
				int temp = arr[i];
				arr[i] = arr[j];
				arr[j] = temp;
				j--;
				i++;
			}

		}
		quicksort(arr, left, j);
		quicksort(arr, i, right);
	}
//快排进阶,在乱序数组中找到排好序后的第几个数,时间大大优化
public static void quicksort(int[] arr, int left, int right, int k) {
		if (left == right) {
			System.out.println(arr[left]);
			return;
		}
		int base = arr[(left + right) / 2];
		int i = left;
		int j = right;
		while (i <= j) {
			while (arr[j] > base) {
				j--;
			}
			while (arr[i] < base) {
				i++;
			}
			if (i <= j) {
				int temp = arr[i];
				arr[i] = arr[j];
				arr[j] = temp;
				i++;
				j--;
			}
		}
		if (k <= j) {
			quicksort(arr, left, j, k);
		} else if (i <= k) {
			quicksort(arr, i, right, k);
		} else {
			quicksort(arr, j + 1, i - 1, k);
		}

	}

*归并排序

基于分治

1.确定分界点:mid=(left+right)/2

2.递归排序left,right

3.归并 --合二为一

private static void merge_sort(int arr[], int left, int right) {
		if (left >= right) {
			return;
		}
		int mid = (left + right) / 2;
		merge_sort(arr, left, mid);
		merge_sort(arr, mid + 1, right);
		int k = 0, i = left, j = mid + 1;
		while (i <= mid && j <= right) {
			if (arr[i] <= arr[j]) {
				temp[k++] = arr[i++];
			} else {
				temp[k++] = arr[j++];
			}
		}
		while (i <= mid) {
			temp[k++] = arr[i++];
		}
		while (j <= right) {
			temp[k++] = arr[j++];
		}
		for (i = left, j = 0; i <= right; i++, j++) {
			arr[i] = temp[j];
		}
	}

堆排序

import java.util.Arrays;
import java.util.Scanner;

public class 堆排序 {
	static int n;

	private static void heapSort(int[] arr) {

		for (int end = n - 1; end > 0; end--) {
			maxheap(arr, end);

			int temp = arr[0];
			arr[0] = arr[end];
			arr[end] = temp;
		}

	}

	private static void maxheap(int[] arr, int end) {

		int lastfa = (0 + end) % 2 == 0 ? (0 + end) / 2 - 1 : (0 + end) / 2;
		for (int fa = lastfa; fa >= 0; fa--) {
			int left = fa * 2 + 1;
			int right = fa * 2 + 2;

			if (right <= end && arr[right] > arr[fa]) {
				int temp = arr[right];
				arr[right] = arr[fa];
				arr[fa] = temp;
			}

			if (arr[left] > arr[fa]) {
				int temp = arr[left];
				arr[left] = arr[fa];
				arr[fa] = temp;
			}

		}

	}

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		n = sc.nextInt();
		int arr[] = new int[n];
		for (int i = 0; i < n; i++) {
			arr[i] = sc.nextInt();
		}
		heapSort(arr);
		System.out.println(Arrays.toString(arr));
	}

}

二分


整数二分

1.找中间值 mid=(l+r)/2

//区间[l,r]被划分为[l,mid]和[mid+1,r]时使用:
int bsearch_1(int l,int r){
    while(l<r){
        int mid=(l+r)/2;
        if(check(mid)){
            r=mid;
        }else{
            l=mid+1;
        }
    }
    return l;
}
//区间[l,r]被划分为[l,mid-1]和[mid,r]时使用:
int  bsearch_2(int l,int r){
    while(l<r){
        int mid=(l+r+1)/2;
        if(check(mid)){
            l=mid;
        }else{
            r=mid-1;
        }
    }
    return l;
}

浮点数二分

//浮点数没有加一减一的问题,所以相对整数二分简单一些
//当范围小于1e-6时认为是解,如要求保留几位小数,则4位—》1e-6,5位-》1e-7
例:求一个数的平方根
    double x;
double l=0,r=x;
while(r-l>1e-6){
    double mid=(l+r)/2;
    if(mid*mid>=x){
        r=mid;
    }else{
        l=mid;
    }
}

java快读和快输模板



import java.io.*;
public class Main {
    private static StreamTokenizer st;                //构建快读模块
    private static int nextInt()throws IOException{
        st.nextToken();
        return (int)st.nval;
    }
    public static void main(String[] args) throws IOException{
        st = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));//初始化 StreamTokenizer
        int N = nextInt(), M = nextInt();//变量N和M使用快读构造方法,构建;
        int tmp;
        int val[] = new int[N+1];
        for (int i=1;i<=M;i++) val[nextInt()]++;
        PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));//构建快输模块

        for (int i=1;i<=N;i++) {
            while(val[i]!=0)  {pw.print(i+" ");val[i]--;}
        }
        pw.close();//关闭,如果还要用,则改为pw.f
    }
}

判断数字是否为回文数

 int s = i, num = 0;
                while (s != 0) {
                    num = num * 10 + s % 10;
                    s /= 10;
                }

快输,快读(BufferedReader)

BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String s = in.read() // 读入一个字符 可读入空格回车 但不抛弃回车
String s1 = in.readLine(); // 读入一行 可读入空格可读入回车 但会将回车抛弃
string s2[] = in.readLine().Split(" "); // 使用Split通过空格分割读入的一行字符串,存在s2中
//输入整数
     5
     1 3 2 5 6
int N = Integer.valueOf(in.readLine());
		int arr[] = new int[n];
		String s = in.readLine();
		String s2[] = s.split(" ");
		for (int i = 0; i < N; i++) {
			arr[i] = Integer.parseInt(s2[i]);
		}
//输入:
    5
    c d a bb e
        
        reader.readLine()//读入第一行的数字
        
        String str = reader.readLine();//读入第二行的字母
        String[] s = str.split(" ");//把第二行的空格切走
        Arrays.sort(s);//对切完后的字符串排序

        StringBuilder sb = new StringBuilder();//把最后结果放在这里面

        for(String st : s){
            sb.append(st);
            sb.append(" ");
        }
        


BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
主要使用 BufferedWriter类中的 write() 类进行输出。 当数据量大的时候一定要使用这个类进行输出,谨记!

  需要注意的是 write() 不能直接输出int类型, 因为write(int a)  会输出其对应的ASCii码的字符 ,比如输出 65 会显示 A 详见代码:

int a = 65;
char b = '2';
String c = "3";
 
out.write(a);
out.write("\n");
out.write(b);
out.write("\n");
out.write(c);
out.write("\n");
out.flush();
 
输出:
A
2
3
 

所以当需要输出一个int类型的变量时, 可以用Integer.toString(int a)方法 将其变为字符串形式输出。

或者使用 + 拼接一个字符串,这样 参数整体就是一个字符串了,比如加一个换行符。详见代码:

int a = 65;
 
out.write(a + "\n");
out.write(Integer.toString(a));
out.flush();
 
输出:
65
65
 //这里要注意在使用out输出之后,如果要换行,不能用System.out.println(),只能用out.writer("\n");来换行
————————————————
版权声明:本文为CSDN博主「Androids_lost_Sheep」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/GD_ONE/article/details/103480407

汉诺塔游戏(递归)

目的:A->B

过程:A->C,C->B=>A->B;

import java.util.Scanner;

public class 汉诺塔游戏 {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		printHanoitower(n, "A", "B", "C");

	}

	static void printHanoitower(int n, String from, String to, String help) {
		if (n == 1) {
			System.out.println("move" + n + "from" + from + "to" + to);
		} else {
			printHanoitower(n - 1, from, help, to);
			System.out.println("move" + n + "from" + from + "to" + to);
			printHanoitower(n - 1, help, to, from);
		}
	}

}

重写compareto()自定义比较基准来进行排序

class job implements Comparable<job> {//构建工作对象
	int s;
	int t;

	public job(int s, int t) {
		this.s = s;
		this.t = t;
	}

	public int compareTo(job other) {//由于要以工作对象的结束时间作为比较排序的基准,这里需要对comparable
                                      //里的comparaTo函数进行重载
                                       //int compareTo(T t)方法说明 
                                        //定义:比较此对象与指定对象的顺序。
                                         //返回:负整数、零或正整数。如果该对象小于、等于或大于指定对象,则
                                          //分别返回负整数、零或正整数。
                                           //假如result返回1。Collections.sort(List)方法就是升序;
                                            //假如result返回-1。Collections.sort(List)方法就是降序;
		int x = this.t - other.t;
		if (x == 0) {
			return this.s - other.s;
		} else {
			return x;
		}
	}

}

java高精度模板题

Input

T输入包括多组 R 和 n。 R 的值占第 1 到第 6 列,n 的值占第 8 和第 9 列。

Output

对于每组输入,要求输出一行,该行包含精确的 R 的 n 次方。输出需要去掉前导的 0 后不要的 0 。如果输出是整数,不要输出小数点。

Sample Input

95.123 12
0.4321 20
5.1234 15
6.7592  9
98.999 10
1.0100 12

Sample Output

548815620517731830194541.899025343415715973535967221869852721
.00000005148554641076956121994511276767154838481760200726351203835429763013462401
43992025569.928573701266488041146654993318703707511666295476720493953024
29448126.764121021618164430206909037173276672
90429072743629540498.107596019456651774561044010001
1.126825030131969720661201
import java.math.BigDecimal;
import java.util.Scanner;

public class Main {
	public static void main(String args[]) {
		Scanner sc=new Scanner(System.in);
		while(sc.hasNext()) { //判断有无下一个输入数据
			BigDecimal x=sc.nextBigDecimal();
			int n=sc.nextInt();
			
			BigDecimal ans=BigDecimal.ONE;
			for(int i=0;i<n;i++) {
				ans=ans.multiply(x);
				//BigDecimal的乘法需要调用multiply()方法,乘以一个BigDecimal对象,返回一个BigDecimal对象
			}
			
			String s=ans.toPlainString();
			//在这里要使用toPlainString()方法,默认的toString()方法在某些情况是科学计数法,错了很多次才知道
			
			/*
			 * 例如样例:
			 * 0.000001 5 
			*/
			
			int len=s.length();
			int leadzero=-1;
			boolean metdot=false;
			for(int i=0;i<len;i++) {
				if(leadzero==-1&&s.charAt(i)!='0') {
					leadzero=i;
					//把整数部分的0去掉
				}
				if(s.charAt(i)=='.') {
					metdot=true;
					//遇到了小数点,说明是小数
					break;
				}
			}
			
			if(metdot==true) {
				s=s.substring(leadzero);
				//遇到了小数点,说明是小数
				len=s.length();
				//重新计算s的长度,因为前面可能截断了整数部分的0
				int releadzero=-1;
				for(int i=len-1;i>=0;i--) {
					if(s.charAt(i)!='0') {
						releadzero=i+1;
						//遇到第一个非零位置,其后的无效0截断
						if(s.charAt(i)=='.') {
							releadzero=i;
							//遇到第一个非零位置是小数点,连小数点也截断
						}
						break;
					}
				}
				s=s.substring(0,releadzero);
			}
			else {
				//没有遇到小数点,是整数,不可能有无效0
				;
			}
			
			System.out.println(s);
		}
		sc.close();
	}
}

重写Arrays.sort()实现从大到小排序

package com.itheimajavase;

import java.util.Arrays;
import java.util.Comparator;

public class Day01 {

    public static void main(String[] args) {

        Integer[] arr = {4, 6, 3, 9, 1, 5, 8};
        Mycomparator c = new Mycomparator();    // 实例化一个Comparator对象
        Arrays.sort(arr, c);
        for(Integer ele : arr) {
            System.out.print(ele +" ");
        }
    }
    // 运行后是从大到小排好序的
}
class Mycomparator implements Comparator<Integer> {
    @Override
    public int compare(Integer o1, Integer o2) {
        if(o1 > o2) // 默认是o1 < o2时返回-1, 一下同理
            return -1;
        if(o1 < o2)
            return 1;
        return 0;
    }
}

进制问题

1<<16==2的16次方

1e6+10=1000010

方块数学小tip

一块长n,宽m的巧克力,能分成边长为x的正方形巧克力s块?s块为多少

公式:s=(n/x)*(m/x)

前缀合tips

一维数组:

//求l到r的和
s[i]=s[i-1]+arr[i]; //初始化前缀和数组
res=s[r]-s[l-1];


//二维前缀和
//利用前缀和
s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+arr[i][j];//初始化
左上点坐标 x1,y1   右上点坐标  x2,y2
//求子矩阵的和
    sum=s[x2][y2]-s[x2][y1-1]-s[x1-1][y2]+s[x1-1][y]
    
//三维前缀和
    

数论

质数判定

质数判定(试除法)

check(i){  //判断是否为质数,时间复杂度O(sqrt(n))
    for(int i=2;i<=n/i;i++){
   if(n%i==0){
   return false;
   }
}
return true
}

分解质因数(试除法)

//时间复杂度O(sqrt(n))

import java.util.Scanner;

public class 质因数分解 {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();

		gcd(n);

	}

	static void gcd(int n) {
		for (int i = 2; i <= n / i; i++) {
			if (n % i == 0) {
				int s = 0;
				while (n % i == 0) {
					n /= i;
					s++;
				}

				System.out.println(i + " " + s);
			}
		}

		if (n > 1) {
			System.out.println(n + " " + 1);
		}
	}
}

质数筛

//埃式筛法,时间复杂度 O(nloglogn)


import java.util.Scanner;

public class 筛质数 {
	static int N = 1000010;
	static int primes[] = new int[N];
	static boolean st[] = new boolean[N];
	static int cnt = 0;

	public static void main(String[] args) {

		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		get_primes(n);
		for (int i = 0; i < cnt; i++) {
			System.out.println(primes[i]);
		}
		sc.close();
	}

	static void get_primes(int n) {
		for (int i = 2; i <= n; i++) {
			if (!st[i]) {
				primes[cnt++] = i;
				for (int j = 2 * i; j <= n; j += i) {
					st[j] = true;
				}
			}
		}
	}
}



//线性筛法
时间复杂度 O(n)
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
import java.util.ArrayList;

public class 线性筛质数 {
	static StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
	static PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));

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

	static ArrayList<Integer> primes = new ArrayList<>();
	static boolean st[];
	static int cnt = 0;

	public static void main(String[] args) throws IOException {
		int n = nextInt();
		int m = nextInt();

		st = new boolean[n + 1];
		get_primes(n);

		while (m-- > 0) {
			int e = nextInt();
			out.write(primes.get(e - 1) + "\n");
		}
		out.flush();

	}

	static void get_primes(int n) {
		for (int i = 2; i <= n; i++) {
			if (!st[i]) {
				primes.add(i);
			}
			for (int j = 0; j < primes.size(); j++) {

				if (primes.get(j) * i > n) {
					break;
				}
				st[primes.get(j) * i] = true;
				if (i % primes.get(j) == 0) {
					break;
				}
			}
		}
	}

}

求约数

// 1、试除法求一个数所有的约数

求最大公约数

欧几里得算法(辗转相除法)

public static int gcd(int m,int n){
    return n==0?m:gcd(n,m%n)
}

裴蜀定理

扩展欧几里得算法

void exgcd(int a,int b,long x,long y){
  if(b==0){
      x.v=1;
      y.v=0;
      return a;
  }
   long d= exgcd(b,a%b,y,x);
    y.v-=(a/b)*(x.v);
    return d
}

class Long{
    long v;
    public Long(long v){
        this.v=v;
    }
}

赛瓦维斯特定理

a , b > 1 , a b 的 最 大 公 约 数 = 1 ( 互 素 ) , 使 得 a x + b y = c , 无 正 整 数 解 的 c 最 大 为 , c = a b − a − b a,b>1,ab的最大公约数=1(互素),使得 ax+by=c,无正整数解的c最大为,c=ab-a-b a,b>1,ab=1使ax+by=ccc=abab

算术基本定理

所有正整数是一定可以唯一的分成为若干个质因子相乘

小tips

1.已知两个互质的数a和b,则这两个数最大不能组成的数为a*b-a-b;

2.求数的上取整:a/b=(a+b-1)/b(a/b上取整)

3.s-v除以n的结果x如果要求为n的倍数,则改式可以转换为,s%n的余数等于v%n的余数

4.(同余式)(a+b)%n=j等价于(a+b)=j(%n);同时一边的数可以加到另一边,符号改变即可–>a=j-b(%n);

判断闰年

5.1 年份能被4整除,但不能被100整除;

5.2 年份能被400整除;

year%100!=0&&year%4==0||year%400==0;

6.防止出现负余数: (a%b+b)%b

6.等差数列前n项和:前n项和公式为:Sn=n*a1+n(n-1)d/2或Sn=n(a1+an)/2

均值不等式

扩展欧几里得算法

此算法求 已知a和b,ax+by=(a和b的最大公约数)的x和y

import java.util.Scanner;

public class test {
	static long x;
	static long y;

	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
		try {
			linearEquation(2, 7, 1);
			System.out.println(x + " " + y);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			System.out.println("无解");
		}
	}

	public static long ext_gcd(long a, long b) {
		if (b == 0) {
			x = 1;
			y = 0;
			return a;
		}
		long res = ext_gcd(b, a % b);
		long x1 = x;
		x = y;
		y = x1 - a / b * y;
		return res;
	}

	public static long linearEquation(long a, long b, long m) throws Exception {
		long d = ext_gcd(a, b);
		if (m % d != 0)
			throw new Exception("无解");
		long n = m / d;
		x *= n;
		y *= n;
		return d;
	}
}

DP问题

闫氏DP分析法

从集合角度分析DP问题

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v9rsF0z3-1653210209939)(D:\桌面\笔记学习\images\闫氏dp.png)]

​ (01背包问题为例)

树状数组

时间复杂度:Olog(n)

用法:

1.单点修改

2.区间查询

树状数组下标必须从1开始

//树状数组的三个基本操作
int a[];  //原数组
int tr[];  //树状数组
int lowbit(int x){
    return x&-x;
}
void add(int x,int v){  //单点修改
    for(int i=x;i<=n;i+=lowbit(i)){
        t[i]+=v;
    }
}

void query(int x){  //区间查询前缀和
    int res=0;
    for(int i=x;i;i-=lowbit(i)){
        res+=tr[i];
    }
}

差分

差分是前缀和的逆运算

给定a[1],a[2],a[3],a[n]

构造差分数组b[N],使得a[i]=b[1]+b[2]+…b[i];

核心操作:将,a[l~r]全部加上c,等价于b[l]+=c,b[r+1]-=c

//一维差分
public class 拆分 {
	static int N = 100010;
	static int a[] = new int[N];
	static int b[] = new int[N];

	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		int m = sc.nextInt();
		for (int i = 1; i <= n; i++) {
			a[i] = sc.nextInt();
			add(i, i, a[i]);
		}
		for (int i = 1; i <= m; i++) {
			int l = sc.nextInt();
			int r = sc.nextInt();
			int e = sc.nextInt();
			add(l, r, e);
		}
		for (int i = 1; i <= n; i++) {
			a[i] = a[i - 1] + b[i];
		}
		for (int i = 1; i <= n; i++) {
			System.out.print(a[i] + " ");
		}
	}

	static void add(int l, int r, int e) {
		b[l] += e;
		b[r + 1] -= e;
	}
}

//二维差分
public class 拆分矩阵 {
	static int N = 1010;
	static int a[][] = new int[N][N];
	static int b[][] = new int[N][N];
	static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
	static BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));

	public static void main(String[] args) throws IOException, InterruptedException {
		String s = in.readLine();
		String s1[] = s.split(" ");
		int n = Integer.valueOf(s1[0]);
		int m = Integer.valueOf(s1[1]);
		int q = Integer.valueOf(s1[2]);
		for (int i = 1; i <= n; i++) {
			String s2 = in.readLine();
			String s22[] = s2.split(" ");
			for (int j = 1; j <= m; j++) {
				a[i][j] = Integer.valueOf(s22[j - 1]);
			}
		}
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= m; j++) {
				insert(i, j, i, j, a[i][j]);
			}
		}
		while (q-- > 0) {
			String s3 = in.readLine();
			String s33[] = s3.split(" ");
			int x1 = Integer.valueOf(s33[0]);
			int y1 = Integer.valueOf(s33[1]);
			int x2 = Integer.valueOf(s33[2]);
			int y2 = Integer.valueOf(s33[3]);
			int e = Integer.valueOf(s33[4]);
			insert(x1, y1, x2, y2, e);
		}
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= m; j++) {
				a[i][j] = a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1] + b[i][j];
			}
		}
		for (int i = 1; i <= n; i++) {
			for (int j = 1; j <= m; j++) {
				out.write(a[i][j] + " ");
			}
			out.write("\n");
		}
		out.flush();

	}

	static void insert(int x1, int y1, int x2, int y2, int e) {
		b[x1][y1] += e;
		b[x1][y2 + 1] -= e;
		b[x2 + 1][y1] -= e;
		b[x2 + 1][y2 + 1] += e;
	}

}

//三维差分  题目三体攻击(蓝桥杯真题)
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;

public class 三体攻击三维差分 {
	static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
	static int N = 1500000;
	static int A, B, C, m;
	static long s[] = new long[N];
	static long b[] = new long[N];
	static long bp[] = new long[N];
	static int d[][] = new int[][] { { 0, 0, 0, 1 }, { 0, 0, 1, -1 }, { 0, 1, 0, -1 }, { 0, 1, 1, 1 }, { 1, 0, 0, -1 },
			{ 1, 0, 1, 1 }, { 1, 1, 0, 1 }, { 1, 1, 1, -1 } };
	static int op[][] = new int[N / 2][7];

	// 映射数组
	static int get(int i, int j, int k) {
		return (i * B + j) * C + k;
	}

	// 判断当前状态有无毁坏的战舰,有则true,无则反之
	static boolean check(int mid) {
		b = Arrays.copyOf(bp, bp.length);
		for (int i = 1; i <= mid; i++) {
			int x1 = op[i][0], x2 = op[i][1], y1 = op[i][2], y2 = op[i][3], z1 = op[i][4], z2 = op[i][5], h = op[i][6];
			b[get(x1, y1, z1)] -= h;
			b[get(x1, y1, z2 + 1)] += h;
			b[get(x1, y2 + 1, z1)] += h;
			b[get(x1, y2 + 1, z2 + 1)] -= h;
			b[get(x2 + 1, y1, z1)] += h;
			b[get(x2 + 1, y1, z2 + 1)] -= h;
			b[get(x2 + 1, y2 + 1, z1)] -= h;
			b[get(x2 + 1, y2 + 1, z2 + 1)] += h;
		}

		Arrays.fill(s, 0);
		for (int i = 1; i <= A; i++)
			for (int j = 1; j <= B; j++)
				for (int k = 1; k <= C; k++) {
					s[get(i, j, k)] = b[get(i, j, k)];
					for (int u = 1; u < 8; u++) {
						int x = i - d[u][0], y = j - d[u][1], z = k - d[u][2], t = d[u][3];
						s[get(i, j, k)] -= s[get(x, y, z)] * t;
					}

					if (s[get(i, j, k)] < 0)
						return true;
				}

		return false;
	}

	public static void main(String[] args) throws IOException {
		String s1 = in.readLine();
		String s11[] = s1.split(" ");
		A = Integer.valueOf(s11[0]);
		B = Integer.valueOf(s11[1]);
		C = Integer.valueOf(s11[2]);
		m = Integer.valueOf(s11[3]);
		String s2 = in.readLine();
		String s22[] = s2.split(" ");
		int pos = 0;
		// 初始化原数组(前缀和数组)
		for (int i = 1; i <= A; i++) {
			for (int j = 1; j <= B; j++) {
				for (int k = 1; k <= C; k++) {
					s[get(i, j, k)] = Integer.valueOf(s22[pos++]);
				}
			}
		}
		// 初始化差分数组
		for (int i = 1; i <= A; i++) {
			for (int j = 1; j <= B; j++) {
				for (int k = 1; k <= C; k++) {
					for (int u = 0; u < 8; u++) {
						int x = i - d[u][0];
						int y = j - d[u][1];
						int z = k - d[u][2];
						int t = d[u][3];
						bp[get(i, j, k)] += s[get(x, y, z)] * t;

					}
				}
			}
		}
		// 进行操作
		for (int i = 1; i <= m; i++) {
			String s3 = in.readLine();
			String s33[] = s3.split(" ");
			for (int j = 0; j < 7; j++) {
				op[i][j] = Integer.valueOf(s33[j]);
			}

		}

		// 进行二分
		int l = 1, r = m;
		while (l < r) {
			int mid = (l + r) >> 1;
			if (check(mid)) {
				r = mid;
			} else {
				l = mid + 1;
			}
		}
		System.out.println(r);

	}
}

映射数组方法

二维数组A×B,映射为一维:下标如果是i,j,则 i×B+j

三维数组A×B×C,映射为一维:下标如果是i,j,k,则(i×B+j)×C+K

Flood Fill

针对网格图

图论问题

一、找 树的直径(一点到另一点的最长路径)

  1. 任取一点x,找到离它最远的点y
  2. 找到离y点最远的点z,点z到y的距离就是这个树的直径

贪心

特点:跳跃性很强,结论证明很难
解决方法:找相似,猜

DFS|BFS

dfs

bfs

// 将bfs看成一个队列,每次取队头元素,找与其相关的元素,添至队尾
// bfs适用与求带“最短”一类的问题
int distan[];// 判重数组兼移动距离数组

	private static int bfs(position start, position end) {
		// TODO Auto-generated method stub
		Queue<position> queue = new LinkedList<>(); // 定义队列
		for (int i = 0; i < r; i++) {
			Arrays.fill(dis[i], -1); // 判重数组初始化
		}
		dis[start.x][start.y] = 0; // 表示起点位置,初始化距离为0
		queue.offer(start); // 将起点放入队列
		int dx[] = { -1, 0, 1, 0 }, dy[] = { 0, 1, 0, -1 }; // 表示偏移量,方向

		while (!queue.isEmpty()) { // 如果当前队列里面不为空,则进行队列操作
			position t = queue.poll(); // 取出队头元素并移除

			for (int i = 0; i < 4; i++) { // 进行移动操作,上下左右

				int x = t.x + dx[i];
				int y = t.y + dy[i];

				if (x < 0 || x >= r || y < 0 || y >= c)
					continue; // 地图越界
				if (gap[x][y] == '#')
					continue; // 遇到障碍
				if (dis[x][y] != -1)
					continue; // 重复经过

				dis[x][y] = dis[t.x][t.y] + 1; // 如果能过到这个位置上来,则这个点走的路程是上个位置走的路程+1

				if (end.x == x && end.y == y)
					return dis[x][y]; // 到终点了

				queue.offer(new position(x, y)); // 将新状态放入队尾
			}
		}
		return -1;
	}

}

//定义位置对象
class position {
	int x, y;

	public position(int x, int y) {
		// TODO Auto-generated constructor stub
		this.x = x;
		this.y = y;
	}
}

数据结构

并查集

操作1:将两个元素合并
操作2:询问两个元素是否在一个集合当中

时间复杂度:近乎O(1)

基本原理:每个集合用一颗树来表示,树根的编号就是整个集合的编号,每个节点存储它的父节点,p[x]表示x的父节点

1.判断树根:if(p[x]==x)

2.求x的集合编号:while(p[x]!=x) x=p[x];

3.合并两个集合:px是x的集合编号,py是y的集合编号–>p[x]=y

模板题:

并查集模板题

//并查集模板
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

public class 并查集 {
	static int N = 10010;
	static int p[] = new int[N];
	static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
	static BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));

	static int find(int x) {  //返回x的祖宗节点,路径压缩,关键
		if (p[x] != x) {
			p[x] = find(p[x]);
		}
		return p[x];
	}

	public static void main(String[] args) throws IOException {
		String s1[] = in.readLine().split(" ");
		int n = Integer.valueOf(s1[0]);
		int m = Integer.valueOf(s1[1]);

		for (int i = 1; i <= n; i++) {  //树的节点初始化,必要步骤
			p[i] = i;
		}

		while (m-- > 0) {
			String s2[] = in.readLine().split(" ");
			int op = Integer.valueOf(s2[0]);
			int a = Integer.valueOf(s2[1]);
			int b = Integer.valueOf(s2[2]);

			if (op == 1) {
				p[find(a)] = find(b);  //合并操作,将a的树根接到b的树根上去
			} else {
				if (find(a) == find(b)) {
					out.write("Y");
					out.write("\n");
				} else {
					out.write("N");
					out.write("\n");
				}
			}
		}
		out.flush();
	}

}


实现单链表

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

public class Main {
	static int N = (int) 1e5 + 10;
	static int e[] = new int[N];  //节点的值
	static int ne[] = new int[N];  //节点的next
	static int head;  // 头结点
	static int index; //当前加入的值的下标
	static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));

	// 链表初始化
	static void init() {
		head = -1;
		index = 0;
	}

	// 在头结点插入
	static void addHead(int value) {
		e[index] = value;
		ne[index] = head;
		head = index;
		index++;
	}

	// 在某个结点后面插入
	static void insert(int a, int value) {
		e[index] = value;
		ne[index] = ne[a];
		ne[a] = index;
		index++;

	}

	// 删除某个结点后面的数
	static void delete(int k) {
		ne[k] = ne[ne[k]];
	}

	public static void main(String[] args) throws IOException {
		int M = Integer.valueOf(in.readLine());
		init();

		while (M-- > 0) {
			String s[] = in.readLine().split(" ");
			if (s[0].equals("H")) {
				int value = Integer.valueOf(s[1]);
				addHead(value);
			} else if (s[0].equals("I")) {
				int a = Integer.valueOf(s[1]);
				int value = Integer.valueOf(s[2]);
				insert(a - 1, value);
			} else if (s[0].equals("D")) {
				int k = Integer.valueOf(s[1]);
				if (k == 0) {
					head = ne[head];
				} else {
					delete(k - 1);
				}
			}
		}

		for (int i = head; i != -1; i = ne[i]) {
			System.out.print(e[i] + " ");
		}
	}

}

树的存储

数是一种特殊的图,连通,无环,所以弄懂图的相关操作之后就可以

// 用邻接表存储

图的存储

// 用邻接表存储


//有向图
int N;// 有几个点
int h[N] // 头结点
int e[N] //当前节点的权值
int ne[N] //节点的next指针
int index=0;

void add(int a,int b){
    e[index]=b;
    ne[index]=h[a];
    h[a]=index;
    index++;
}
 
Arrays.fill(h,-1) //初始化每一个单链表的头结点为-1

//无向图
 
无向图跟有向图步骤一样,只是在add的步骤上,再多加一步,add(a,b),add(b,a);

图的遍历

// 深度优先遍历(深度优先搜索)
boolean st[];// 遍历时不能重复,所以要有一个判重数组

int dfs(int u){
    st[u]=true;  //st[u] 表示点u已经被遍历过了
    
    for(int i=h[u];i!=-1;i=ne[i]){
        int j=e[i];
        if(!st[j]){
            dfs(j);
        }
    }
    
}

// 宽度优先遍历

Queue <integer> queue=new ArraysList<>();

st[1]=true;  //表示1号点已经被遍历过
queue.offer(1);

while(!queue.isEempty()){
    int t=queue.poll();
    for(int i=h[t];i!=-1;i=ne[i]){
        int j=e[i];
        if(!st[j]){
            st[j]=true;
            queue.offe
        }
    }
}

图的最短路径

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值