Java实现蓝桥杯第十二届2021年JavaB组真题

花已经枯了 浇再多的水也没用

C 直线

【题目描述】

在这里插入图片描述

【思路】

区域中的点两两组合,根据斜截式计算出每条直线的k和b。
难点在于对于 <k,b>怎么不重不漏地统计数量。
这里想到用 Map<Double, Set>,注意k、b可能为小数。相同的k映射到 同一个Set中,用Set判去重复的b。

package 第十二届;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

/**
* @author JohnnyLin
* @version Creation Time:2021年5月9日 上午8:52:55
* 类说明
*/
public class _C直线 {
	
//	static int N = 2, M = 3;
	static int N = 20, M = 21;
	static Map<Double, Set<Double>> map = new HashMap<>();
	
	//计算斜率
	public static double getK(int x1, int y1, int x2, int y2) {
		return (double) (y1 - y2) /(x1 - x2) ;
	}
	
	
	public static void main(String[] args) {
		
		for(int i = 0; i < N; i ++) {
			for(int j = 0; j < M; j ++) {
				//(i, j) --> (x, y)
				for(int x = 0; x < N; x ++) {
					
					if( i == x) continue;
					for(int y = 0; y < M; y ++) {
						if( y == j) continue;
						double k = getK(i, j, x, y);
						double b = j - k * i;
						if( !map.containsKey(k) ) {
							System.out.println(k);
							Set<Double> set = new HashSet<>();
							set.add(b);
							map.put(k, set);
						}else {
							Set<Double> set = map.get(k);
							set.add(b);
						}
					}
				}
				
			}
		}
		int ans = 0;
		for(Entry<Double, Set<Double>> entry: map.entrySet()) {
			ans += entry.getValue().size();
		}
		//47753
		System.out.println( ans + N + M);

	}

}

package 第十二届;


import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

class Line{
    double k, b;
    public Line(double kk, double bb){
        this.k = kk;
        this.b = bb;
    }
    //重写equals方法: 比较k和b是否相等
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Line line = (Line) o;
        return Double.compare(line.k, k) == 0 &&
                Double.compare(line.b, b) == 0;
    }

    @Override
    public int hashCode() {
        //使用包装类
        Double kk = this.k;
        Double bb = this.b;
        int result = 1;
        result = 31 * result + (kk == null ? 0 : kk.hashCode());
        result = 31 * result + (bb == null ? 0 : bb.hashCode());
        return  result;
    }

}
public class _C直线2 {
	static int N = 20, M = 21;
	static Set<Line> set = new HashSet<>();
	
	//计算斜率
	public static double getK(int x1, int y1, int x2, int y2) {
		return (double) (y1 - y2) /(x1 - x2) ;
	}
    public static void main(String[] args) {
    	for(int i = 0; i < N; i ++) {
			for(int j = 0; j < M; j ++) {
				//(i, j) --> (x, y)
				for(int x = 0; x < N; x ++) {
					
					if( i == x) continue;
					for(int y = 0; y < M; y ++) {
						if( y == j) continue;
						double k = getK(i, j, x, y);
						double b = j - k * i;
						set.add(new Line(k, b) );
					}
				}
				
			}
		}
    	
    	// 47753
    	System.out.println(set.size() + N + M);
    }
}

看了一下其他大佬给的答案,我写的不对,据说是精度问题(四舍五入),所以结果多了。正确答案是:40257

正解

【思路】
需要通过Math.ads(k1-k2)>1e-8 || Math.ads(b1-b2)>1e-8 |来判断不同的直线。
排序
正确答案是:40257

package 第十二届.right;


import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

class Line implements Comparable<Line>{
    double k, b;
    public Line(double kk, double bb){
        this.k = kk;
        this.b = bb;
    }
	@Override
	public int compareTo(Line o) {
		if( Double.compare(this.k, o.k) == 0 ) return Double.compare(this.b, o.b);
		return Double.compare(this.k, o.k);
	}

}
public class _C直线3 {
	static int N = 20, M = 21;
//	static int N = 2, M = 3;
	static Line[] q = new Line[1000000];
	static double esp = 10E-8;
	//计算斜率
	public static double getK(int x1, int y1, int x2, int y2) {
		return (double) (y1 - y2) /(x1 - x2) ;
	}
    public static void main(String[] args) {
    	int t = 0;
    	for(int i = 0; i < N; i ++) {
			for(int j = 0; j < M; j ++) {
				//(i, j) --> (x, y)
				for(int x = 0; x < N; x ++) {
					
					if( i == x) continue;
					for(int y = 0; y < M; y ++) {
						if( y == j) continue;
						double k = getK(i, j, x, y);
						double b = j - k * i;
						q[t ++] = new Line(k, b);
					}
				}
				
			}
		}
		//排序
    	Arrays.sort(q, 0, t);
    	
    	int res = 1; //k :{1,1,2,2,3,3}  {1,2,3}
    	for( int i = 1; i < t; i ++)
    		if( Math.abs(q[i].k - q[i - 1].k ) > esp || Math.abs(q[i].b - q[i - 1].b ) > esp)
    			res ++;
    	
    	System.out.println(res + N + M);
    }
}

D 货物摆放

在这里插入图片描述

【思路】
三个数a、b、c,乘积是n。n的约数中选三个,乘积是n的组合有多少。

当时写的时候觉得很难,觉得非dp写不出来。看了y总的解答后, 原来暴力也可以,是自己太菜了,多刷题、多刷题、多刷题。
重要事情讲三遍。

答案: 2430

package 第十二届;

import java.util.ArrayList;
import java.util.List;

/**
* @author JohnnyLin
* @version Creation Time:2021年5月9日 下午12:59:44
*/
public class _D货物摆放 {
	
	static long  N = 2021041820210418L;
	
	static List<Long> f =  new ArrayList<>();
	
	public static void main(String[] args) {
		/**
		 * N = 4时, i = 1  2
		 * 			N/i  4  2
		 */
		for(long i = 1; i * i <= N; i ++) {
			if( N % i== 0) {
				f.add(i);
				//
				if( N / i != i) f.add(N / i);
			}
		}
		long ans = 0;
		for(long a: f)
				for(long b: f)
					for(long c : f) {
						if( a * b * c == N) ans++;
					}
		System.out.println(ans);

	}

}

E路径

在这里插入图片描述【思路】
spaf

package 第十二届;

import java.util.Arrays;

/**
* @author JohnnyLin
* @version Creation Time:2021年5月9日 下午1:43:36
*/
public class _E路径 {
	static int N = 2200, M = N *50;//N: 点数 	M:边数
	static int h[] = new int [N];
	static int e[] = new int [M];
	static int w[] = new int [M];
	static int ne[] = new int [M];
	static int idx, n;
	
	static int q[] = new int[N];
	static int dist[] =  new int[N];
	static boolean st[] = new boolean[N];
	 static int INF = 0x3f3f3f3f;
	public static int gcd(int a, int b) {
		return b == 0 ? a : gcd(b, a % b);
	}
	
	public static void add(int a, int b, int c) {//添加一条边a -> b ,边的权重为c
		e[idx] = b;
		w[idx] = c;
		ne[idx]= h[a];
		h[a] = idx ++;
	}
	
	//求1号点到n号点的最短路径
	public static void spfa() {
		int hh = 0, tt = 0;
		Arrays.fill(dist, INF);
		dist[1] = 0;
		q[tt ++] = 1;
		st[1] = true;
		
		while( hh != tt) {
			int t = q[hh ++];
			if( hh == N) hh = 0;
			st[t] = false;
			
			for(int i = h[t]; i != -1; i = ne[i]) {
				int j = e[i];
				if( dist[j] > dist[t] + w[i]) {
					dist[j] = dist[t] + w[i];
					 if (!st[j])     // 如果队列中已存在j,则不需要将j重复插入
		                {
		                    q[tt ++ ] = j;
		                    if (tt == N) tt = 0;
		                    st[j] = true;
		                }
				}
			}
		}
		
	}
	

	public static void main(String[] args) {
		n = 2021;
		Arrays.fill(h, - 1);
		for(int i = 1; i <= n; i ++) {
			for(int j = Math.max(1, i - 21); j <= Math.min(i + 21, n); j ++) {
				//(i,j)节点
				int d = gcd(i, j);
				add(i, j, i * j / d);
				
			}
		}
		spfa();
		System.out.println(dist[n]);
	}

}

时间显示

【题目描述】
在这里插入图片描述
【思路】
时间换算 注意 1s = 1000ms即可

import java.util.Scanner;

public class Main{
    public static void main(String args[]){
        
        Scanner reader = new Scanner(System.in);
        long time = reader.nextLong();
        //1618708103123
        //24*60 * 60s  
        // 1s = 1000ms
        long fact = 3600 * 1000;  
        long  f = 24 * fact; // 一天的毫秒数
        long h = time % f / fact ;
        // 1 min = 60 s
        long m = time % f % fact/ 60000;
        
        long s = time % f % fact % 60000 / 1000;
        System.out.printf("%02d:%02d:%02d", h, m, s);
    }
}

G 最少砝码

在这里插入图片描述
在这里插入图片描述

看了别人的题解,还是一脸懵逼

import java.util.Scanner;

public class Main {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		long n = sc.nextLong();
		sc.close();
		long w = 1L;
		long i = 1L;
		long s = 1L;
		while (s < n) {
			w *= 3;
			s += w;
			i++;
		}
		System.out.println(i);
	}
}

H 杨辉三角

在这里插入图片描述

【思路】
找规律

组合数和杨辉三角关系:C(i,j)对应杨辉三角的第i行第j列(i从0开始, j从0开始)

写出杨辉三角:
在这里插入图片描述

发现左右对称( 因为 C(a, b) == C(a, a - b) ),左半边肯定是优先查找到目标数N的,所以只看左半边即可:
在这里插入图片描述发现规律:

  1. 每一个斜行自右上到左下递增(即C(n,k), n逐行递增,k(斜行)不变,其结果C(n,k)递增)
  2. 同一横行中,从左到右递增,最右边最大且为组合数C(k, 2* k)

那么查找目标数N,即从最下边的斜线开始查找。第一个 找到的位置即为N第一次出现的位置。
根据组合公式: 32! / 16! /16! = 601 080 390
34!/17!/17!= 2 333 606 220 > 10e9,所以只需枚举前16个斜行,即k枚举到16即可。
因此可以直接从中间对称轴倒序二分查找:

二分的左右端点为:
左端点l:2*k
右端点r:max(n, l)

import java.util.Scanner;

public class Main {

    static int n;
    //计算组合数
    public static long C(int a, int b){
        long res = 1;
        
        //C(a, b) = a! / (a - b)! / b!  =  a*(a - 1)*a(a - 2)……*a(a - b + 1)  / b! 
        for(int i = a, j = 1; j <= b; j ++, i --){
            res = res * i / j;
            //该值已经大于n了 无意义且防止爆long
            if( res > n)   return res;
        }
        return res;
    }
    public static boolean check(int k){
        //二分该斜行k
        
        int l = 2* k, r = Math.max(n,l);
        while( l < r){
            int mid = l + r >> 1;
            if( C(mid, k) >= n ) r = mid;
            else l = mid + 1;
        }
        if( C(r, k) != n ) return false;
        
        //C(r, k)对应编号  (r + 1) *r / 2 + k + 1
        long ans =  (long)(r + 1) *r /2 + k + 1;
        System.out.println(ans);
        return true;
            
    }
	public static void main(String[] args) {
		Scanner reader = new Scanner(System.in);
		n =  reader.nextInt();
		
		//从第16斜行开始枚举
		for(int k = 16; ; k --){
		    if( check(k) ) break;
		}

		

	}

}

  • 7
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值