蓝桥杯备考冲刺 直线,杨辉三角,路径

题目一 : 2021年蓝桥杯填空题——直线

C:直线
【问题描述】
​ 在平面直角坐标系中,两点可以确定一条直线。如果有多点在一条直线上,那么这些点中任意两点确定的直线是同一条。
​ 给定平面上 2 × 3 2 × 32×3 个整点 ( x , y ) ∣ 0 ≤ x < 2 , 0 ≤ y < 3 , x ∈ Z , y ∈ Z {(x,y)|0 ≤ x < 2,0 ≤ y < 3, x ∈ Z,y ∈ Z}(x,y)∣0≤x<2,0≤y<3,x∈Z,y∈Z,即横坐标是 0 00 到 1 11 (包含 0 00 和 1 11) 之间的整数、纵坐标是 0 00 到 2 22 (包含 0 00 和 2 22) 之间的整数的点。这些点一共确定了 11 1111 条不同的直线。
​ 给定平面上 20 × 21 20 × 2120×21 个整点 ( x , y ) ∣ 0 ≤ x < 20 , 0 ≤ y < 21 , x ∈ Z , y ∈ Z {(x,y)|0 ≤ x < 20,0 ≤ y < 21, x ∈ Z,y ∈ Z}(x,y)∣0≤x<20,0≤y<21,x∈Z,y∈Z,即横坐标是 0 00 到 19 1919 (包含 0 00 和 19 1919) 之间的整数、纵坐标是 0 00 到 20 2020 (包含 0 00 和 20 2020) 之间的整数的点。请问这些点一共确定了多少条不同的直线。


【答案】
40257

【难点】

①本题的难点个头决定在于精度的处理,非常容易处理不好

【思路】

①利用直线方程 y = kx + b,但是不是直接利用,由于考虑到精度,

        k = (y2-y1) / (x2-x1);

        b = (y1*x2 - x1*y2)。

(此时这里就有两个大坑——

        1、分子分母需要 * 1.0否则循环出来都是整数;

        2、分母/分子会出现除不尽的情况,这时候我们需要把分母和分子最简化,比如 9/12 ->         3/4,涉及最大公约数,这是最后优化精度的一个关键点)。

②就是利用HashMap,键存储斜率k,值是一个HashSet集合存储截距b。

③然后直接遍历,4个for循环解决。

【代码】

public class LongStraight {
	
	public static void main(String[] args) {
		int res = 0;
		//利用 y = kx + b;
		//斜率不存在的直线数 即 x的点数 20(0-19)
		res = 20;
		
		//用map存储
		Map> map = new HashMap<>();
		double k = 0; //斜率
		double b = 0; //截距
		HashSet set = new HashSet<>();
		
		for (int x1 = 0; x1 < 20; x1++) {
			for (int y1 = 0; y1 < 21; y1++) {
				for (int x2 = 0; x2 < 20; x2++) {
					for (int y2 = 0; y2 < 21; y2++) {
						//斜率存在
						if (x1 != x2) {
							//这里是有个坑的,x1 x2 y1 y2都是整数,需要 *1.0变成双精度
							int muk = (x2 - x1);
							int zik = (y2 - y1);
							int tk = gcd(muk, zik);
							k = (zik/tk) * 1.0 / (muk/tk);
							
                            //b = y1 - k * x1;    //精度太差,但是往往考试时很容易就这么写
							int mub = (x2 - x1);
							int zib = (y1*x2 - x1*y2);
							int tb = gcd(mub, zib);
							b = (zib/tb) * 1.0 / (mub/tb);
							
							set = map.getOrDefault(k, new HashSet());
							set.add(b);
							map.put(k, set);
						}
					}
				}
			}
		}
		for (HashSet resSet : map.values()) {
			res += resSet.size();
		}
		System.out.println(res);    //40257 -- 就是答案
	}
	//最大公约数
	static int gcd(int a, int b) {
		return b == 0? a : gcd(b, a%b);
	}
}

题目二 :2021年蓝桥杯编程大题——杨辉三角

H:杨辉三角形
【问题描述】
​ 图形是著名的杨辉三角形:

​ 如果我们按从上到下、从左到右的顺序把所有数排成一列,可以得到如下数列:

​ 1 , 1 , 1 , 1 , 2 , 1 , 1 , 3 , 3 , 1 , 1 , 4 , 6 , 4 , 1 , . . . 1, 1, 1, 1, 2, 1, 1, 3, 3, 1, 1, 4, 6, 4, 1, ...1,1,1,1,2,1,1,3,3,1,1,4,6,4,1,...

​ 给定一个正整数 N,请你输出数列中第一次出现 N 是在第几个数?

【思路参考】

http://H:杨辉三角形 【问题描述】 ​ 下面的图形是著名的杨辉三角形: ​ 如果我们按从上到下、从左到右的顺序把所有数排成一列,可以得到如下数列: ​ 1 , 1 , 1 , 1 , 2 , 1 , 1 , 3 , 3 , 1 , 1 , 4 , 6 , 4 , 1 , . . . 1, 1, 1, 1, 2, 1, 1, 3, 3, 1, 1, 4, 6, 4, 1, ...1,1,1,1,2,1,1,3,3,1,1,4,6,4,1,... ​ 给定一个正整数 N,请你输出数列中第一次出现 N 是在第几个数? ———————————————— 版权声明:本文为CSDN博主「en_oc」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/TraceOfTwilight/article/details/116072583

首先杨辉三角,我们要的数据其实只有一半,当前迭代的时候也需要另一半,上面的文章二个key点:

1、卡好到第三列(数据为前n项和),利用n*(n+1)/2 < 10亿(最大用例数)得n<44721,当我把前面的44721层都暴力搜完了,还是找不到这个数,说明这个数一定会在第二列,为什么?

        因为第三列及后面到(n+1)/2列的全部数据都大于10亿了,再搜没有意义了,同时也不够空间去搜了,而第一列全部都是1,所以一定在第二列,第二列就是一个1,1,2,3,4,5,。。。的数列,利用等差数列,直接可以求出位置 n*(n+1)/2。

2、有点动态规划的feeling,就是创建的一维数组,我们只需在每层遍历时,从后往前即可,这样内存也不会太过。

//从第三层开始遍历——对应索引2
        for (int i = 2; i < nums.length; i++) {
          //从后往前遍历,就可以刚刚好地替代旧数据且不会使用到新数据,j=0的位置一直为1不需更新
          for (int j = i; j > 0; j--) {
            nums[j] += nums[j-1];
            if (nums[j] == n) {     //注意我们这里找到的是后往前的数据,对称的j坐标才是我们想要
              System.out.println(i - j + 1 + k);
              return;
            }
          }
          k += i + 1;
        }

【当然这里有个注意点】

就是一定得用Long类型的,包括接受的参数n,数组nums,否则就会出现负数等无法预料的情况。

/*
n为int类型(位数不够)
100000777
-769133009

n为long类型(正解)
100000777
5000077750302255

*/

【代码】

import java.util.Scanner;

public class Main {
	public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        Long n = scan.nextLong();
        scan.close();

        if (n == 1) {
          System.out.println(n);
          return;
        }

        long[] nums = new long[44721];
        nums[0] = 1;
        nums[1] = 1;
        Long k = 3L;  //至第二层共3个数
        //从第三层开始遍历——对应索引2
        for (int i = 2; i < nums.length; i++) {
          //从后往前遍历,就可以刚刚好地替代旧数据且不会使用到新数据,j=0的位置一直为1不需更新
          for (int j = i; j > 0; j--) {
            nums[j] += nums[j-1];
            if (nums[j] == n) {     //注意这里找到的是后往前的数据,对称的j坐标才是我们想要
              System.out.println(i - j + 1 + k);    //比如 1 3 3 1
              return;                               //我们搜到的是索引2,但是其实我们需要1
            }                                       // i+1 - j  
          }
          k += i + 1;
        }

        //前44721层都找不到,那么必定在44722层后的第二列,因为此时其他3-n/2+1列的数值已超过十亿
        // 数n会在第n+1层的第2列 位数为:前n层的数据数的前n项和 n*(n+1)/2
        System.out.println(n*(n+1)/2 + 2);
    }
}

题目三:2021年蓝桥杯填空——路径
【问题描述】
​ 小蓝学习了最短路径之后特别高兴,他定义了一个特别的图,希望找到图
中的最短路径。
​ 小蓝的图由 2021 个结点组成,依次编号 1 至 2021。
​ 对于两个不同的结点 a, b,如果 a 和 b 的差的绝对值大于 21,则两个结点之间没有边相连;如果 a 和 b 的差的绝对值小于等于 21,则两个点之间有一条长度为 a 和 b 的最小公倍数的无向边相连。
​ 例如:结点 1 和结点 23 之间没有边相连;结点 3 和结点 24 之间有一条无向边,长度为 24;结点 15 和结点 25 之间有一条无向边,长度为 75。
​ 请计算,结点 1 和结点 2021 之间的最短路径长度是多少。


【答案】
10266837

【思路】

显然的两种最短路方法Floyd,Dijsktra,推荐上使用第二种,时间快,毕竟题目是单源最短路。

public class Main {
	public static void main(String[] args) {
		//题目确定了首尾节点 1 2021,属于单源最短路径,可以有floyd或者dijsktra
		//首先创建邻接矩阵 1、对角元为0即可,也符号实际意义   2、邻接矩阵是对称的,出于时间考虑,我们直接算上三角,遍历时赋给下三角即可
		// 2021*2021都不足以用long,所以我们这里不必考虑长整型这类
		int[][] near = new int[2021][2021];
		int min = Integer.MAX_VALUE / 2;
		for (int i = 0; i < near.length; i++) {
			for (int j = i+1; j < near.length; j++) {
				if (j - i > 21) {
					near[i][j] = min;
				} else {
					near[i][j] = lcm(i+1, j+1);
				}
				near[j][i] = near[i][j];
			}
		} 
		//floyd(near);      //10266837
		dijsktra(near, 0);  //10266837
		System.out.println(near[0][2020]);
	}
	// 单源最短路,且必须为正权值,但速度快 o(n^2)
	static void dijsktra(int[][] near, int start) {
		int[] visited = new int[near.length];   //记忆数组
		visited[start] = 1; //标记起点以开始循环
		
		//循环n-1次,仅仅是为了循环n-1次,k无实际意义
		for (int k = 1; k < near.length; k++) {
			int curMin = Integer.MAX_VALUE / 2;
			int t = start;       //记录中间节点
			//寻找最短边的尾点最为中间节点
			for (int i = 0; i < near.length; i++) {
				if (visited[i] == 0 && near[start][i] < curMin) {
					curMin = near[start][i];
					t = i;
				}
			}
			visited[t] = 1; //标记
			//更新
			for (int j = 0; j < near.length; j++) {
				if (visited[j] == 0) {
					near[start][j] = Math.min(near[start][j], near[start][t] + near[t][j]);
				}
			}
			
		}
	}
	//多源最短路,时间偏长,但可用于有负权图的情况
	static void floyd(int[][] near) {
		// o(n^3)  有点暴力解的感觉
		//中间节点开始循环
		for (int k = 0; k < near.length; k++) {
			for (int i = 0; i < near.length; i++) {
				for (int j = 0; j < near.length; j++) {
					near[i][j] = Math.min(near[i][j], near[i][k] + near[k][j]);
				}
			}
		}
	}
	//最小公倍数
	static int lcm(int a, int b) {
		return a / gcd(a, b) * b;
	}
	//最大公约数
	static int gcd(int a, int b) {
		return b == 0 ? a : gcd(b, a%b);
	}
}

【总结】

①蓝桥杯的题目一定要注意好精度和位数,int类型甚至可以直接无脑改成long类型,特别是数据用例过一亿的那种。

你可能感兴趣的:(java,eclipse,动态规划,算法)