藍橋杯演算法模板常用套路及API等個人總結

一葉之修發表於2019-03-23

寫這篇部落格的今天是19年的省賽考前的最後一天了,想寫一些小白萌新們受益的東西

 

從最常考的全排列開始吧~

全排列模板1,最常用的寫法

public class 全排列_模板1 {

	public static void main(String[] args) {
		dfs(0);
		System.out.println(ans);//9的全排有362880種
	}
	
	static int[] a = new int[] {1,2,3,4,5,6,7,8,9};
	
	static int n=9,ans=0;
	
	static void dfs(int m) {
		if(m>=n) {
			System.out.println("一些核心的操作 比如ans:"+ans);
			ans++;
			for(int i=0;i<n;i++)
				System.out.print(a[i]+" ");
			System.out.println();
			
			return;
		}
		
		for(int i=m;i<n;i++) {
			swap(i,m);
			dfs(m+1);
			swap(i,m);//回溯
		}
	
	
	}
	
	static void swap(int i,int j) {
		int t = a[i];
		a[i] = a[j];
		a[j] = t;
	}

}

 

全排列模板2,使用標記陣列的寫法

public class 全排列_模板2 {

	public static void main(String[] args) {
		vis = new boolean[n];
		b = new int[n];
		dfs(0);
		System.out.println(ans);//9的全排有362880種
	}
	
	static int[] a = new int[] {1,2,3,4,5,6,7,8,9};
	static int[] b;
	static boolean[] vis;
	static int n=9,ans=0;
	
	static void dfs(int m) {
		if(m>=n) {
			System.out.println("一些核心的操作 比如ans:"+ans);
			ans++;
			for(int i=0;i<n;i++)
				System.out.print(b[i]+" ");
			System.out.println();
			
			return;
		}
		
		for(int i=0;i<n;i++) 
			if(!vis[i]) {
				vis[i] = true;
				b[m] = a[i];
				dfs(m+1);
				b[m] = 0;//其實這個也可以不用回溯,它會被覆蓋
				vis[i] = false;//回溯
			}
	
	}

}

 

下面是需要排重的全排_模板

在上面模板上使用了雜湊表查重,把一些陣列並接成字串

import java.util.HashSet;

public class 全排列_模板3 {

	public static void main(String[] args) {
		dfs(0);
		System.out.println(ans);//9的全排有362880種
		System.out.println(set.size());//1680
	}
	
	static int[] a = new int[] {1,1,1,2,2,2,3,3,3};
	
	static int n=9,ans=0;
	static HashSet<String> set = new HashSet<>();
	
	static void dfs(int m) {
		if(m>=n) {
			System.out.println("一些核心的操作 比如ans:"+ans);
			ans++;
			String s="";
			for(int i=0;i<n;i++) {
				s+=a[i];
				System.out.print(a[i]+" ");
			}
			System.out.println();
			set.add(s);
			return;
		}
		
		for(int i=m;i<n;i++) {
			swap(i,m);
			dfs(m+1);
			swap(i,m);//回溯
		}
	
	}
	
	static void swap(int i,int j) {
		int t = a[i];
		a[i] = a[j];
		a[j] = t;
	}

}

 

使用雜湊表查重後是無序的,那麼遇到需要列印字典序的有重複元素的全排呢

leetcode 46. 全排列

leetcode 47. 全排列 II

子集的寫法也和全排很相似

leetcode 78. 子集(Subsets) beat 100%

leetcode 90. 子集 II(Subsets II) beat 98.57%

 

 

 

判斷閏年

	static boolean is(int x) {
		return x%400==0 || (x%4==0 && x%100!=0);
	}

 

 

篩素數O({\color{DarkBlue} n^{\tfrac{3}{2}}})

	static boolean is(int n) {
		if(n==1)
			return false;
		for(int i=2;i*i<=Math.sqrt(n);i++)
			if(n%i==0)
				return false;
		
		return true;
	}

 

倍篩法_素數O(nlogn)

public class 倍篩模板 {

	public static void main(String[] args) {
		boolean[] is = new boolean[1000005];
		is[1] = true;//true表示不是素數
		for(int i=2;i<1000005;i++)
			if(!is[2])
				for(int j=2*i;j<1000005;j+=i)
					is[j] = true;
		System.out.println(is[2004]);
	}

}

 

01揹包

public class _01揹包 {

	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		int m = in.nextInt();//大小
		int n = in.nextInt();//物品數
		int[] dp = new int[m+5];//開揹包質量的大小
		for(int i=1;i<=n;i++) {
			int v = in.nextInt();//物品體積
			int w = in.nextInt();//物品價值
			for(int j=m;j>=v;j--)
				dp[j] = Math.max(dp[j], dp[j-v]+w);
		}
		System.out.println(dp[m]);
	}

}

 

完全揹包

import java.util.Scanner;

public class _完全揹包 {

	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		int m = in.nextInt();//大小
		int n = in.nextInt();//物品數
		int[] dp = new int[m+5];//開揹包質量的大小
		for(int i=1;i<=n;i++) {
			int v = in.nextInt();//物品體積
			int w = in.nextInt();//物品價值
			for(int j=v;j<=m;j++)
				dp[j] = Math.max(dp[j], dp[j-v]+w);
		}
		System.out.println(dp[m]);
	}

}

 

 

最小公倍數gcd

	static int gcd(int a,int b) {
		return b==0?a:gcd(b,a%b);
	}

最大公約數lcm

	static int lcm(int a,int b) {
		return a*b/gcd(a,b);
	}

多個數共同的gcd和lcm模板

	static int gcd(int[] a) {
		int n = a.length;
		int g = a[0];
		for(int i=1;i<n;i++)
			g = gcd(g,a[i]);
		
		return g;
	}
	
	static int lcm(int[] a) {
		int n = a.length;
		int l = a[0];
		for(int i=1;i<n;i++)
			l = lcm(l,a[i]);
		
		return l;
	}

二分答案_模板

藍橋杯 2017年第八屆真題 分巧克力 經典的二分答案 輸入掛

	static void f(int[] a) {
		int n = a.length;
		int l=0,r=n-1,ans=0;//一般上界需要自己構造
		while(l<=r) {
			int mid = l + (r-l)/2;
			if(ok(a[mid])) {
				r = mid-1;
				ans = mid;
			}else
				l = mid+1;
		}
		System.out.println(ans);
	}
	
	static boolean ok(int x) {
		return false;//按照題意寫
	}

二分lower_bound的實現(找到第一個大於等於給定值key的那個數)如[1,2,2,3]查詢2,會返回第一個2的下標

    public static int lowerBound(int []nums,int l,int r,int target){
        while(l<=r){//r=n-1
            int m = (l+r)/2;
            if(nums[m]>=target) r= m-1;
            else    l = m +1;
        }
        return l;
    }

upper_bound的實現(要求在按照非遞減順序排好序的陣列中找到第一個大於給定值key的那個數)(不常用)

    public static int upperBound(int []nums ,int l,int r, int target){
        while(l<r){//其中 r = nums.length
            int m = (l+r)/2;
            if(nums[m]<=target) l = m+1;
            else    r = m;
        }
        return l;
    }

 

各種排序實現

  • 氣泡排序(學會手寫)
  • 選擇排序(沒什麼應用)
  • 插入排序(對近乎有序的陣列複雜度提升到O(n))
  • 計數排序(給定區間排序)
  • 歸併排序(求逆序數)
  • 快速排序(找第K大數)
  • 堆排序(優先佇列API)
  • 希爾排序(插入排序的升級版)

 

大數類BigInteger和BigDecimal及方法API

考場上查API

 

 

進位制問題,如有價值1,2,4,8等價值的硬幣,如何用最小的硬幣數湊出100元

	static int f(int n,int k) {//呼叫API轉成k進位制,缺點題目有上界為8
		int ans=0;
		String s = Integer.toString(n, k);
		System.out.println(s);
		for(int i=0;i<s.length();i++)
			ans+=s.charAt(i)-'0';
		
		return ans;
	}

頭條面試題:找錢問題,共1024,64,16,4,1幾種面值 

    static void solve(){
        Scanner cin = new Scanner(System.in);
        int N = 1024-cin.nextInt(), ans = 0;
        for(int i=0; i<4; i++){
            ans += N/arr[i];
            N %= arr[i];
        }
        System.out.println(ans);
    }
!

堆的應用,給定一個只包括 '('')''{''}''['']' 的字串,判斷字串是否有效

leetcode 20. 有效的括號(Valid Parentheses)

    public boolean isValid(String s) {
    	if(s==null||s.equals(""))
    		return true;
 
        char[] ch = s.toCharArray();
        Stack<Character> stack = new Stack<>();
        
        for(int i=0;i<ch.length;i++)
        {
        	if(ch[i]=='(' || ch[i]=='[' || ch[i]=='{')
        		stack.push(ch[i]);
        	else if(ch[i]==')' || ch[i]==']' || ch[i]=='}')
        	{
        		if(!stack.isEmpty())
        		{
	        		char p = stack.pop();
	        		if(p=='(' && ch[i]==')')
	        			continue;
	        		if(p=='[' && ch[i]==']')
	        			continue;
	        		if(p=='{' && ch[i]=='}')
	        			continue;
        		}
        		return false;
        	}
        }
        
        return stack.empty();
    }

 

快速冪及矩陣快速冪模板

矩陣冪_快速冪_矩陣乘法_模板

	static long mpow(long a,long n) {//快速冪,mod一般題目給1e9+7
		if(n==0 || a==1)
			return 1;
		long ans=1;
		while(n!=0) {
			if(n%2==1)
				ans = a*ans%mod;
			a=a*a%mod;
			n>>=1;
		}
		
		return ans;
	}

 矩陣乘法模板

//這裡是矩陣的乘法模板,預設方陣
	static int[][] mul(int[][] a,int[][] b){
		int n = a.length;
		int[][] ans = new int[n][n];
		for(int i=0;i<n;i++)
			for(int j=0;j<n;j++)
				for(int k=0;k<n;k++)
					ans[i][j] =(ans[i][j] + a[i][k]*b[k][j])%MOD;//ans[i][j] += a[i][k]*a[k][j];
		return ans;
	}

 矩陣快速冪模板


	//矩陣快速冪_模板
	static int[][] mpow(int[][] a,int n){
		int N = a.length;
		int[][] ans = new int[N][N];
		for(int i=0;i<N;i++)
			ans[i][i] = 1;//初始化為單位矩陣W
		while(n>0) {//寫 n!=0也行,會快些麼,反正位運算是會快的.這個不清楚
			if((n&1)==1) 
				ans = mul(ans,a);
			a = mul(a,a);
			n>>=1;
		}
		return ans;
	}

 

階乘逆元求組合數C(n,m)模板_O(nlogn)

	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		n = in.nextInt();
		f = new long[n+5];
		inv = new long[n+5];
		f[0]=1;
		for(int i=1;i<n+5;i++) {
			f[i]=f[i-1]*i%mod;
			inv[i] = mpow(f[i],mod-2);
		}
		
		
	}
	
	static int n;
	static long mod = 1000000009;
	static long[] f;
	static long[] inv;
	
	static long C(int n,int m) {
		return f[n]*inv[m]%mod*inv[n-m]%mod;
	}
	
	static long mpow(long a,long n) {//快速冪
		if(n==0 || a==1)
			return 1;
		long ans=1;
		while(n!=0) {
			if(n%2==1)
				ans = a*ans%mod;
			a=a*a%mod;
			n>>=1;
		}
		
		return ans;
	}

 

 

尤拉函式模板        定義:小於n的正整數中與n互質的數的數目(φ(1)=1)

	static int Euler(int n) {//O(n) 有時候需要long
		int ans=n;
		
		for(int i=2;i<=n;i++) 
			if(n%i==0) {
				ans=ans/i*(i-1);
				while(n%i==0)
					n/=i;
			}
		
		return ans;
	}
	static int Euler(int n) {//O(sqrt(n))
		int ans=n;

		for(int i=2;i*i<=n;i++) 
			if(n%i==0) {
				ans=ans/i*(i-1);////n*(1-(1/p))轉化為n/p*(p-1)
				while(n%i==0)
					n/=i;
			}
		if(n>1)//優化 O(sqrt(n)) 不過要在出口 if(n>1)ans/n*(n-1) O(n)不用
			ans=ans/n*(n-1);
		
		return ans;
	}

 

字首和模板

import java.util.Scanner;

public class 字首和模板 {

	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
		int n = in.nextInt();
		int[] sum = new int[n+1];
		for(int i=1;i<=n;i++)
			sum[i] = sum[i-1] + in.nextInt();//前i個數的和

	}

}

 

線段樹模板

 

 

並查集模板

	static int n,m,cnt=0;
	static int[] f = new int[10005];//初始化一般是f[i]=i
	
	static int find(int x) {
		if(f[x]==x)
			return x;
		return f[x] = find(f[x]);
	}
	
	static void union(int x,int y) {
		int a = find(x);
		int b = find(y);
		if(a!=b) {
			f[a] = b;
			cnt++;
		}
	}

 

dfs求聯通塊模板

	static int n,m;
	static int[][] vis;
	static char[][] ch;

	static int[] dx = new int[] {1,1,1,0,0,-1,-1,-1};
	static int[] dy = new int[] {0,-1,1,1,-1,1,0,-1};
	
	static void dfs(int t,int x,int y) {// hdu1241 油田問題 八聯通
		vis[x][y] = t;
		
		for(int i=0;i<8;i++) 
			if(x+dx[i]>=0 && x+dx[i]<n && y+dy[i]>=0 && y+dy[i]<m && ch[x+dx[i]][y+dy[i]]=='@' && vis[x+dx[i]][y+dy[i]]==0)
				dfs(t,x+dx[i],y+dy[i]);
		
	}
			int t=0;
			for(int i=0;i<n;i++)
				for(int j=0;j<m;j++)
					if(ch[i][j]=='@' && vis[i][j]==0)
						dfs(++t,i,j);
			System.out.println(t);

最小生成樹模板

	static int n,m,cnt=0,ans=0;
	static int[] f;
	
	static int find(int x) {
		if(f[x]==x)
			return x;
		return f[x] = find(f[x]);
	}
	
	static int union(int x,int y) {
		int a = find(x);
		int b = find(y);
		if(a!=b) {
			f[a] = b;
			return 1;
		}
		return 0;
	}
	
	
	static class Edge implements Comparable<Edge>{
		int a,b,c;
		public Edge(int a,int b,int c) {
			this.a = a;
			this.b = b;
			this.c = c;
		}
		@Override
		public int compareTo(Edge o) {
			return this.c - o.c;
		}
	}
	public static void main(String[] args) {
		InputReader in = new InputReader(System.in);  //用它來代替Scanner。
		while(true) {
			n = in.nextInt();
			if(n==0)
				break;
			m = n*(n-1)/2;
			cnt=0;
			ans=0;
			f = new int[n+5];
			for(int i=1;i<=n;i++)
				f[i] = i;
			
			PriorityQueue<Edge> pq = new PriorityQueue<>();
			
			for(int i=1;i<=m;i++) {
				int a = in.nextInt();
				int b = in.nextInt();
				int c = in.nextInt();
				int d = in.nextInt();
				if(d==1)
					cnt+=union(a,b);
				else
					pq.add(new Edge(a, b, c));
			}
			
			while(!pq.isEmpty()) {
				Edge u = pq.poll();
				if(union(u.a, u.b)==1) {
					cnt++;
					ans+=u.c;
					if(cnt==n-1)
						break;
				}
			}
			
			System.out.println(ans);
		}
		
		
	}

輸入掛模板

	//解決javaIO讀取過慢的方法,利用該類讀取輸入資料,不要用Scanner!!!
	static class InputReader {
		public BufferedReader reader;
		public StringTokenizer tokenizer;

		public InputReader(InputStream stream) {
			reader = new BufferedReader(new InputStreamReader(stream), 32768);
			tokenizer = null;
		}

		public String next() {
			while (tokenizer == null || !tokenizer.hasMoreTokens()) {
				try {
					tokenizer = new StringTokenizer(reader.readLine());
				} catch (IOException e) {
					throw new RuntimeException(e);
				}
			}
			return tokenizer.nextToken();
		}

		public int nextInt() {
			return Integer.parseInt(next());
		}

	}

 

拓撲排序模板

藍橋杯 卡勒沃夫之弱水路三千(提高型) 拓撲排序+Map

		static ArrayList<Integer> list = new ArrayList<>();
	    static ArrayList<Integer>[] edge = new ArrayList[105];	
        
		while(m-->0) {//建圖 鄰接表 並且用w[i]記錄入度
			String s1 = in.next();
			String s2 = in.next();
			if(!map.containsKey(s1))
				map.put(s1,n++);
			if(!map.containsKey(s2))
				map.put(s2,n++);
			w[map.get(s2)]++;
			edge[map.get(s1)].add(map.get(s2));
		}

    	for(int i:map.values())//把邊為0的放進佇列
				if(w[i]==0)
					q.add(i);
			while(!q.isEmpty()) {
				int u = q.poll();
				list.add(u);
				for(int v:edge[u]) {
					w[v]--;
					if(w[v]==0)
						q.add(v);
				}
			}

dijkstra模板O(n^2)

 

 

dijkstra鄰接表+優先佇列模板O(nlogn)

 

 

 

 

 

 

 

 

相關文章