蓝桥杯尤其喜欢考察 全排列和组合。DFS、BFS、自定义比较器排序、简单递推。偶尔考考二分、数论。
package 常用算法模板;
/**
* @author JohnnyLin
* @version Creation Time:2020年10月15日 下午10:53:40
*/
public class 全排列_不带重复元素的_抓 {
static int[]a= {1,2,3,4,5};
static boolean vis[]=new boolean[a.length];
static int ans;
public static void dfs(int m,String s) {
if(m==a.length) {
//s记录一个排列方案
System.out.println(s);
//ans记录排列方案数目
ans++;
return;
}
for(int i=0;i<a.length;i++) {
if(!vis[i]) {
vis[i]=true;
dfs(m+1,s+a[i]+" ");
vis[i]=false;
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
dfs(0,"");
//A(5,5)=120
System.out.println(ans);
}
}
另一种写法
import java.util.Scanner;
public class Main{
static int N = 10, n ;
static boolean st[] = new boolean[N];
static int[] f = new int[N];
public static void dfs(int u){
if( u == n){
for(int i = 0; i < n; i ++)
System.out.print(f[i]+" ");
System.out.println();
return;
}
for(int i = 1; i <= n; i++)
if(!st[i]){
st[i] = true;
f[u] = i;
dfs( u + 1);
st[i] = false;
}
}
public static void main(String args[]){
Scanner reader = new Scanner(System.in);
n = reader.nextInt();
dfs(0);
}
}
package 常用算法模板;
import java.util.Arrays;
/**
* @author JohnnyLin
* @version Creation Time:2020年10月15日 下午11:00:44
*/
public class 全排列_带重复元素的_抓 {
static int a[]= {2,1,2,3,3};
static boolean vis[]=new boolean[a.length];
static int ans;
static void dfs(int m,String s) {
if(m==a.length) {
//记录每一个排列方案
System.out.println(s);
//记录方案数目
ans++;
}
for(int i=0;i<a.length;i++) {
if(!vis[i]) {
if(i>0&&a[i-1]==a[i]&&!vis[i-1]) continue;
vis[i]=true;
dfs(m+1,s+a[i]+" ");
vis[i]=false;
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Arrays.sort(a);
dfs(0, "");
System.out.println(ans);
}
}
import java.util.Scanner;
import java.util.Arrays;
/*
带重复元素的全排列
*/
public class Main{
//1 2 2 3 3
static int a[]= {2,1,2};
static int n = a.length;
static boolean st[] = new boolean[n];
static int [] f= new int[n];
public static void dfs(int u){
if( u == n){
for(int i = 0; i < n; i ++)
System.out.print(f[i]+" ");
System.out.println();
return;
}
for(int i = 0; i < n; i ++){
if( !st[i]){
//当前元素与前一个元素相同 且 前一个元素没有用过
if( i > 0 && a[i] == a[i - 1] && !st[i - 1]) continue;
st[i] = true;
f[u] = a[i];
dfs( u + 1);
st[i] = false;
}
}
}
public static void main(String args[]){
//Scanner reader = new Scanner(System.in);
//n = reader.nextInt();
Arrays.sort(a, 0, n);
dfs(0);
}
}
LeetCode传送门
LeetCode全排列、全排列II
package 常用算法模板;
import java.util.ArrayList;
import java.util.List;
/**
* @author JohnnyLin
* @version Creation Time:2020年10月15日 下午11:53:16
*/
public class 子集_不带重复元素的 {
static int []a= {1,2,3};
public static void dfs(int m,List<Integer> sub,List<List<Integer>> res) {
res.add(new ArrayList<>(sub));
for(int i=m;i<a.length;i++) {
sub.add(a[i]);
dfs(i+1,sub,res);
sub.remove(sub.size()-1);
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
List<List<Integer>> res=new ArrayList<>();
List<Integer> sub=new ArrayList<>();
dfs(0, sub, res);
//每一种子集
for(List<Integer> list:res){
System.out.println(list.toString());
}
//子集方案数
System.out.println(res.size());
}
}
import java.util.*;
/*
不带重复元素的子集
*/
public class Main{
static int a[]= {1,2,3,4,5};
static int n = a.length;
public static void main(String args[]){
int n = a.length;
Set<List<Integer>> ans = new HashSet<>();
for(int i = 0; i <= n; i ++){ //枚举长度 [0,n]
for(int j = 0; j <= n - i; j ++){//枚举左端点[0,n - i]
int k = j + i;//右端点 [i, n]
List<Integer> sub = new ArrayList<>();
//枚举左右区间断点 [j, k)
for(int m = j; m < k; m ++) sub.add(a[m]);
ans.add(sub);
}
}
for(List list : ans)
System.out.println(list);
}
}
package 常用算法模板;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author JohnnyLin
* @version Creation Time:2020年10月15日 下午11:53:16
*/
public class 子集_带重复元素的 {
static int []a= {2,1,2};
public static void dfs(int m,List<Integer> sub,List<List<Integer>> res) {
res.add(new ArrayList<>(sub));
for(int i=m;i<a.length;i++) {
if(i>m&&a[i-1]==a[i]) continue;
sub.add(a[i]);
dfs(i+1,sub,res);
sub.remove(sub.size()-1);
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
List<List<Integer>> res=new ArrayList<>();
List<Integer> sub=new ArrayList<>();
Arrays.sort(a);
dfs(0, sub, res);
//每一种子集
for(List<Integer> list:res){
System.out.println(list.toString());
}
//子集方案数
System.out.println(res.size());
}
}
LeetCode传送门:
LeetCode子集、子集II的三种解法
static int mod=100000007;
/**
* 计算a的n次方(要求n>=0)
* @param a
* @param n
* @return
*/
public static long quickPow(long a,int n) {
long ans=1;
while(n>0) {
if((n&1)==1)
ans=ans*a%mod;
a=a*a%mod;
n>>=1;
}
return ans;
}
//一般矩阵运算结果都会比较大 会有取模要求
static final long mod=10000;
/**
* 矩阵乘法
* @param a
* @param b
* @return 返回矩阵a与矩阵相乘的结果
*/
public static long [][] matrixMutiply(long [][]a,long b[][]) {
int n=a.length;
int m=b[0].length;
int q=a[0].length;
if(q!=b.length)
throw new IllegalArgumentException();
long c[][]=new long[n][m];
for(int i=0;i<n;i++) {
for(int j=0;j<m;j++) {
for(int k=0;k<q;k++) {
//若有取模要求: c[i][j]=(c[i][j]%mod+a[i][k]*b[k][j]%mod)%mod;
c[i][j]+=a[i][k]*b[k][j];
}
}
}
return c;
}
/**
* 矩阵快速幂
* @param a
* @param n
* @return 返回n个矩阵a相乘的结果
*/
public static long [][] matrix_Pow(long [][]a, int n) {
int m=a.length;
long [][]ans=new long[m][m];
//注意:这里是单位矩阵
for(int i=0;i<m;i++)
ans[i][i]=1;
while(n>0) {
if((n&1)==1)
ans=matrixMutiply(ans, a);
a=matrixMutiply(a, a);
n>>=1;
}
return ans;
}
二分查找符合条件的最大值:经典问题是蓝桥杯2017第八届java B组分巧克力
public static int binarySerachTarget(int []a,int low,int high,int target) {
int ans=1;
while(low<=high) {
int mid=(low+high)>>1;
if(check(mid)) {
low=mid+1;
ans=mid;
}else high=mid-1;
}
//ans即为答案
return ans;
}
//根据题目写check函数
private static boolean check(int mid) {
return false;
}
/**
* @param a
* @param l
* @param r
* @param target
* @return
* 返回第一个大于等于target的元素下标
*/
public static int lowe_bound(int []a,int l,int r,int target) {
while(l<=r) {
int mid=(l+r)>>1;//或l+(r-l);
if(a[mid]>=target)
r=mid-1;
else
l=mid+1;
}
return l;
}
public static boolean isLeapYear(int y) {
//能被400整除 H或能被4整除但是不能被100整除
return (y%4==0&&y%100!=0)||(y%400==0);
}
public static boolean isPrime(int n) {
if(n==1)return false;
for(int i=2;i*i<=n;i++) {
if(n%i==0)return false;
}
return true;
}
static final int N=100000005;
static int []st=new int[N];
//埃氏筛法:一个质数的倍数一定是合数,筛掉合数剩下的自然就是素数
//得到n以内的素数
public static void selcetPrime(int n) {
for(int i=2;i<=n;i++) {
if(st[i]==0) {//是素数 一开始是2 2是素数 就筛出所有的2的倍数
for(int j=2*i;j<=n;j+=i) {//筛去i的倍数
st[j]=1;
}
}
}
int res=0;
//统计素数个数 1不是素数 从2开始统计
for(int i=2;i<=n;i++) {
if(st[i]==0) {
res++;
System.out.println(i);
}
}
//合数个数:因为统计是 从2开始 因此要加上1这个合数
System.out.println(n-res+1);
///素数个数:res
System.out.println(res);
}
/**
* @param a
* @param b
* @return 返回a、b的最大公约数
*/
public static int gcd(int a,int b) {
return (b==0)?a:gcd(b,a%b );
}
public static int gcd(int a,int b) {
return (b==0)?a:gcd(b,a%b );
}
/**
* @param a
* @param b
* @return 返回a、b的最小公倍数
*/
public static int lcm(int a,int b) {
//最小倍数*最大公约数即为最小公倍数
return a*b/gcd(a, b);
}
public static boolean isValid(String exp) {
if(exp==null||exp.equals("")) return true;
char e[]=exp.toCharArray();
Stack<Character> stack=new Stack<>();
for(int i=0;i<e.length;i++) {
if(e[i]=='<'||e[i]=='{'||e[i]=='('||e[i]=='[')//左括号则入栈
stack.push(e[i]);
else {
//取出栈顶左括号与现有右括号匹配
//取前要判断栈非空
if(!stack.isEmpty()) {
Character c=stack.peek();
stack.pop();
if(c=='<'&&e[i]=='>')continue;
if(c=='{'&&e[i]=='}')continue;
if(c=='('&&e[i]==')')continue;
if(c=='['&&e[i]==']')continue;
}
return false;
}
}
//若左右括号匹配 则栈为空返回真值
return stack.empty();
}
static int a[]= {1,5,36,7,0,52,67,5};
//1 6 42 49 49 101 168 173
static int []preSum;
public static void prefixSum() {
int len=a.length;
preSum=new int[len];
preSum[0]=a[0];
for(int i=1;i<len;i++) {
preSum[i]=preSum[i-1]+a[i];
}
}
preSum[j]-preSum[i]查询(i,j]区间的区间和
使用前缀和可以O(1)操作查询区间和,O(n)操作更新。对于在计算过程中需要更新原数据的这种区间求和,可以使用区间树实现O(logn)更新,O(logn)查询。
package 常用算法模板;
import java.util.Scanner;
/**
* @author JohnnyLin
* @version Creation Time:2020年10月16日 下午3:48:00
*/
class SegTree{
//左右子树
SegTree lson,rson;
//区间范围
int l,r;
int sum;
public SegTree(int l,int r) {
this.l=l;
this.r=r;
}
}
public class 线段树区间求和 {
static int [] a={1,5,36,7,0,52,67,5};
static int [] b=new int[a.length];;
static int [] ans;
static SegTree root;
/**
* @param l
* @param r
* @return 根据给定区间建立[l,r]范围的线段树
*/
public static SegTree buildsegTree(int l,int r) {
SegTree segTree=new SegTree(l, r);
if(l==r) {
segTree.sum=a[l];
return segTree;
}
int mid=(l+r)>>1;
SegTree lson=buildsegTree(l, mid);
SegTree rson=buildsegTree(mid+1, r);
segTree.lson=lson;
segTree.rson=rson;
segTree.sum=lson.sum+rson.sum;
return segTree;
}
/**
* @param tree
* @param x
* @param y
* @return 返回[x,y]区间和
*/
public static int query(SegTree tree,int x,int y) {
int l=tree.l;
int r=tree.r;
if(x<=l&&y>=r) return tree.sum;
int mid=(l+r)>>1;
int ans=0;
if(x<=mid) ans+=query(tree.lson, x, y);
if(y>mid) ans+=query(tree.rson, x, y);
return ans;
}
/**
* @param tree
* @param p 下标
* @param i 值
* 修改了原数据需要,需要更新线段树
* 时间复杂度:log(n)
*/
public static void update(SegTree tree,int p,int i) {
if(tree==null)//叶子结点的下一层了
return;
//更新节点的sum
tree.sum+=i;
int l=tree.l;
int r=tree.r;
int mid=(l+r)>>1;
if(p<=mid) update(tree.lson, p, i);
else update(tree.rson, p, i);
}
public static void main(String[] args) {
Scanner reader=new Scanner(System.in);
//建立与原数组长度相对的区间树
root=buildsegTree(0, a.length-1);
//1 6 42 49 49 101 168 173
//[i,j] {1,5,36,7,0,52,67,5}
System.out.println(query(root,3,6));//126
System.out.println(query(root,0,0));//0
System.out.println(query(root,0,2));//6
//修改a数值元素值
a[4]=11;
update(root, 4, 11);
System.out.println(query(root,3,6));//6
}
}