写在开篇:
蓝桥杯大赛即将到来,倒计时36天!
do(学习真题+理解真题+会做真题+总结真题)
while(时间!=0)
宝剑锋从磨砺出,梅花香自苦寒来。
争取冲出省赛,加油!
今天带来的是第十二届B组的两道真题的个人题解,如果有不当之处,还请各位朋友指正,下面就让我们开始吧。
C.直线(十二届真题)
解题思路
本题也是一个数学问题,需要回忆之前学过的数学知识,例如如何计算斜率和求截距。
解题思路是先获得所有的点,再根据两两不同的点去计算直线,用的斜截式 y = kx + b和两点式(y1-y2)=k(x1-x2),然后得到所有的 k 和 b,并去重,去重我用的是HashSet
1,对于点坐标的表示,我是用的是x扩大100倍来和y进行分隔。
for(int i=0;i<=19;i++){
for(int j=0;j<21;j++){
set.add(i*100+j);
}
}
2,获取点x,y坐标的方法如下(a,b分别为点的坐标)
int x1=a/100,x2=b/100,y1=a%100,y2=b%100;
3,获取了点的x,y坐标后,我们就可以来计算斜率了,这里用的是两点式。
int up=y1-y2,down=x1-x2;
int c1=gcd(up,down);
String k=(up/c1)+" "+(down/c1);//这里求的是斜率,这个斜率表示有点奇怪,就是x的差值和y的差值拼凑
这里我们我们还需要用一下最大公约数,因为例如up=4,down=2,和up=8,down=4,计算出来的斜率是一样的,这俩种情况的取值可能会出现在一条直线上。
//返回值则是a和b的最大公约数
int gcd(int a,int b){
return b == 0 ? a:gcd(b,a%b);
}
4,我们还要将斜率不存在的情况单独拿出来讨论。
if(down==0){//判断斜率不存在的直线
count++;//这个变量是我用来统计有多少条垂直于x轴的直线,HashSet类型的ans用来去重
ans.add("x="+x1);
continue;
}
5.接下来我们来计算截距。
//下面的步骤会涉及到求直线方程的俩点式和点斜式,要注意这俩个公式的结合使用,不然可能第一眼看完会后可能会有一点懵
//上面我们求的了斜率,我们随便带入一个点来求截距
int kb=y1*down-up*x1;
int c2=gcd(kb,down);
String B=(kb/c2)+" "+(down/c2);//求的是截距
ans.add(k+" "+B);//(k+" "+B)这样一个字符串用来表示一条直线
完整代码
public static int gcd(int a,int b) {
return b==0?a:gcd(b,a%b);
}
static int count=0;
public static void main(String[] args) {
HashSet<Integer> set=new HashSet<>();
HashSet<String> ans=new HashSet<>();
for(int i=0;i<=19;i++){
for(int j=0;j<21;j++){
set.add(i*100+j);
}
}
List<Integer> arr=new ArrayList<>(set);
int len=arr.size();
for(int i=0;i<len;i++){
int a=arr.get(i);
for(int j=i+1;j<len;j++){
int b=arr.get(j);
int x1=a/100,x2=b/100,y1=a%100,y2=b%100;
int up=y1-y2,down=x1-x2;
int c1=gcd(up,down);
String k=(up/c1)+" "+(down/c1);//这里求的是斜率,这个斜率表示有点奇怪,就是x的差值和y的差值拼凑
if(down==0){//判断斜率不存在的直线
count++;
ans.add("x="+x1);
continue;//将垂直于x轴的直线添加进去,应该是有19条
}
//下面的步骤会涉及到求直线方程的俩点式和点斜式,要注意这俩个公式,不然可能第一眼看完会懵
//上面我们求的了斜率,我们随便带入一个点来求截距
int kb=y1*down-up*x1;
int c2=gcd(kb,down);
String B=(kb/c2)+" "+(down/c2);
ans.add(k+" "+B);
}
}
System.out.println(count);
System.out.println(ans.size());
}
本题难度总体来说还是不大的,就是写题之前要将数学知识好好回顾一下感觉。
E.路径(十二届真题)
解题思路
看完题就懵了,熟悉,但不会(尬笑),听过很多次它的大名——最短路径,然后就去恶补了一波求最小路径的方法。
本题解用的是Floyd(弗洛伊德)方法。
下面介绍一下这个算法的思想:
(一)算法思想:
Floyd算法是一个经典的动态规划算法。用通俗的语言来描述的话,首先我们的目标是寻找从点i到点j的最短路径。从动态规划的角度看问题,我们需要为这个目标重新做一个诠释(这个诠释正是动态规划最富创造力的精华所在)。从任意节点i到任意节点j的最短路径不外乎2种可能,一是直接从i到j,二是从i经过若干个节点k到j。所以,我们假设Dis(i,j)为节点u到节点v的最短路径的距离,对于每一个节点k,我们检查Dis(i,k) + Dis(k,j) < Dis(i,j)是否成立,如果成立,证明从i到k再到j的路径比i直接到j的路径短,我们便设置Dis(i,j) = Dis(i,k) + Dis(k,j),这样一来,当我们遍历完所有节点k,Dis(i,j)中记录的便是i到j的最短路径的距离。
知道该用的算法后,本题就比较好解了,直接套用这个算法思想的模板就好(当然模板需要不断敲才能熟悉,还是要coding(哭笑)),下面直接展示代码了。
完整代码
public class 第五题路径 {
static int[][] graph = new int[2022][2022];
static final int INF = 0x3f3f3f3f;//这个数表示的是无穷大
private static void floyd() {
for (int k = 1; k <= 2021; k++) {
for (int i = 1; i <= 2021; i++) {
for (int j = 1; j <= 2021; j++) {
if (i != j && graph[i][j] > graph[i][k] + graph[k][j]) {
graph[i][j] = graph[i][k] + graph[k][j];
}
}
}
}
}
private static int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a % b);
}
public static void main(String[] args) {
for (int i = 1; i <= 2021; i++) {
for (int j = 1; j <= 2021; j++) {
graph[i][j] = INF;
}
}
for (int i = 1; i <= 2021; i++) {
int st = Math.max(i - 21, 1);//这么写是因为差值<=21的点之间才有直接相连的边
for (int j = st; j <= i; j++) {
int div = gcd(j, i);
int lcm = i * j / div;//无向边的大小
graph[i][j] = lcm;
graph[j][i] = lcm;
}
}
floyd();
System.out.println(graph[1][2021]); // 10266837
}
}
结果要跑一会才可以出来。
总结:现阶段独立自主解出来还是比较困难,继续努力。
明天继续,冲冲冲!