今天的比赛题目是今年蓝桥杯的模拟赛原题,所以题解大概是可以在网上给搜索到的,但是希望同学们都是本着一个公平公正的态度来打这一场比赛的,这一场比赛大概前两题做出来,代码填空做对,编程大题大概一题半就可以拿到省一了,因为这次蓝桥杯官方的模拟赛,比赛连接在这里蓝桥杯模拟赛
A:题意杀,可能很多同学把18xx里面的x当成了题目描述中的x,实际上两者是没有关系的,给出这样一个东西实际上是给出你数字的范围是1800~1899,这样可以避免出现多种情况,题意明白了这道题目就很简单了,大概按照题意去算一下就可以了,答案是1806
#include <bits/stdc++.h> using namespace std; int main() { int i; for(int i=1;i<=99;i++){ if(i*i-i >=1800&&i*i-i <= 1899){ cout<<i*i-i<<endl;break; } } return 0; }
B:典型的蓝桥杯暴力题,按照题意枚举就可以了,写一个cot数组保存每个数字出现的数字就可以了,答案算出来是40096
#include <bits/stdc++.h> using namespace std; int cot[10]; bool check(int a,int b){ int a1 = a/100,a2 = (a%100)/10,a3 = a%10; int b1 = b/100,b2 = (b%100)/10,b3 = b%10; cot[a1]++;cot[a2]++;cot[a3]++;cot[b1]++;cot[b2]++;cot[b3]++; int x1 = a*b3,x2 = a*b2,x3 = a*b1; int sum = a*b; if(x1 < 100||x1 > 999||x2 < 100||x2 > 999||x3 < 100||x3 > 999||sum > 99999||sum < 10000) return false; cot[x1/100]++;cot[(x1%100)/10]++;cot[x1%10]++; cot[x2/100]++;cot[(x2%100)/10]++;cot[x2%10]++; cot[x3/100]++;cot[(x3%100)/10]++;cot[x3%10]++; cot[sum/10000]++;cot[(sum%10000)/1000]++;cot[(sum%1000)/100]++; cot[(sum%100)/10]++;cot[sum%10]++; for(int i = 0;i < 10;i++){ if(cot[i]!=2) return false; } return true; } int main() { for(int i = 100;i <= 999;i++){ for(int j = 100;j <= 999;j++){ memset(cot,0,sizeof(cot)); if(check(i,j)){ cout<<i*j<<endl; return 0; } } } return 0; }
C:这道题目应该是比较简单的(前提是你得知道康托展开,一个蓝桥杯经常考的算法),下面简单解释一下康托展开:
康托展开其实就是一种特殊的哈希函数
把一个整数X展开成如下形式:X=a[n]*n!+a[n-1]*(n-1)!+...+a[2]*2!+a[1]*1!
其中a为整数,并且0<=a<i,i=1,2,..,n
{1,2,3,4,...,n}表示1,2,3,...,n的排列,如 {1,2,3} 按从小到大排列一共6个。123 132 213 231 312 321 。代表的数字 1 2 3 4 5 6 也就是把10进制数与一个排列对应起来。他们间的对应关系可由康托展开来找到。如我想知道321是{1,2,3}中第几个大的数可以这样考虑 :第一位是3,当第一位的数小于3时,那排列数小于321 如 123、 213 ,小于3的数有1、2 。所以有2*2!个。再看小于第二位2的:小于2的数只有一个就是1 ,所以有1*1!=1 所以小于321的{1,2,3}排列数有2*2!+1*1!=5个 。所以321是第6个大的数。 2*2!+1*1!是康托展开。再举个例子:1324是{1,2,3,4}排列数中第几个大的数:第一位是1小于1的数没有,是0个 0*3! 第二位是3小于3的数有1和2,但1已经在第一位了,所以只有一个数2,1*2! 。第三位是2,小于2的数是1,但1在第一位,所以有0个数 0*1! ,所以比1324小的排列有0*3!+1*2!+0*1!=2个,所以1324是第三个大数,不知道大家有没有看明白这个康托展开的解释,那么接下来我们要怎样去用代码实现这样的一个康托展开呢?当然,康拓展开的时间复杂度是O(n^2)的,因为是蓝桥杯,所以,放心大胆的去艹吧
//参数int s[]为待展开之数的各位数字,如需展开2134,则s[4]={2,1,3,4}. int fac[]= {1,1,2,6,24,120,720,5040,40320,362880}; //... long cantor(int s[],int n) { int i,j,temp,num; num=0; for(i=0; i<n; i++){ temp=0; for(int j=i+1; j<n; j++){ if(s[j]<s[i]) temp++; } num+=fac[n-i]*temp; } return (num+1); }那么当我们知道了康拓展开之后我们就能很轻松的解决这道题了,由于这道题目中的排序是字符,所以我们将其认为是ASCLL码就可以了,另外,由于数字特别大,需要处理为long long
#include <bits/stdc++.h> using namespace std; const int N = 17; int main() { char tmp[] = "bckfqlajhemgiodnp"; long long x = 1; long long sum = 0; int cnt = 0; for(int i = 1; i < N; i++) { x *= i;//用于求阶乘 cnt = 0;//算出后面的序列有几个比当前tmp[i] for(int j = N-i; j < N; j++) { if(tmp[N-i-1]>tmp[j]) { cnt++; } } sum += cnt*x;//求和 } cout << sum; return 0; }
D、E:蓝桥杯典型的代码填空题,这种题目实际上你把他的代码拿出来,然后在填空处放上你的代码,自己跑一下就知道到底自己做没做对了,因为评测环境不同,所以这道题可能一些同学的答案是对的,所以给出答案,大家自己去对照一下:答案是t>0?t+1:t-1,实际上就是去算答案到底是正值还是负值,因为t的值就代表了两个串之间的大小关系,++ --其实就是简单的去算位置而已,当然答案会有很多种写法,所以,不一定这就是标准答案
F:蓝桥杯的第一道编程大题一定是一道暴力的傻逼题,所以,这道题我们只需要枚举然后不断算取最接近的值,当遇到绝对值反超时,就break,具体解法看代码:
public class Main { public static void main(String[] args) { Scanner sn = new Scanner(System.in); double m = sn.nextDouble()/100/12; int month = sn.nextInt(); int money = 10000*100;//用分来表示 int min = money/month; int ans = 0x7fffffff,last = 0x7fffffff;//分别表示结果和绝对值最小的那个值 for(int i = min;;i++){ int moneys = money; for(int j = 1;j<=month;j++){//从最小还款可能枚举 moneys = get(moneys*(1+m)-i); } if(Math.abs(last)>Math.abs(moneys)){ last = moneys; ans = i; }else break;//如果绝对值反超,直接break } System.out.println(ans); sn.close(); } private static int get(double money) { return (int)(money+0.5);//因为已经表示为分,所以加上小数点后一位就能判断是否需要进位。 } }
#include <bits/stdc++.h> using namespace std; int get(double money){ return (int)(money+0.5);//因为已经表示为分,所以加上小数点后一位就能判断是否需要进位。 } int main(){ double m; int month; scanf("%lf %d",&m,&month); m /= 1200; int money = 10000*100;//用分来表示 int min = money/month; int ans = 0x7fffffff,last = 0x7fffffff;//分别表示结果和绝对值最小的那个值 for(int i = min;;i++){ int moneys = money; for(int j = 1;j<=month;j++){//从最小还款可能枚举 moneys = get(moneys*(1+m)-i); } if(abs(last)>abs(moneys)){ last = moneys; ans = i; }else break;//如果绝对值反超,直接break } printf("%d",ans); return 0; }
G:这道题目实际上是hihocoder上的一道原题,也是google当年网招的一道签到题(所以蓝桥杯的组委会经常会搞出原题这种事情),这道题其实我们只需要考虑一些特殊情况,而且因为数字只有10个数,所以我们可以通过dfs来完成这道题目:
import java.math.BigInteger; import java.util.Arrays; import java.util.Scanner; public class Main { static boolean[][] a = new boolean[10][10]; static boolean[] b = new boolean[10]; static int sum; static int n; static int[][] c; static int[] d = new int[11]; public static void main(String[] args) { Scanner input = new Scanner(System.in); a[1][3] = true; a[1][7] = true; a[1][9] = true; a[2][8] = true; a[3][9] = true; a[3][7] = true; a[4][6] = true; a[7][9] = true; n = input.nextInt(); c = new int[n][2]; for(int i=0;i<n;i++){ c[i][0] = input.nextInt(); c[i][1] = input.nextInt(); } f(1,1); System.out.println(sum); } public static boolean check(int i){ int j,k; for(j=0;j<n;j++){ for(k=1;k<=i;k++){ if(d[k]==c[j][0]||d[k]==c[j][1]){ if(d[k]==c[j][0]){ if(d[k-1]==c[j][1]) break; }else{ if(d[k-1]==c[j][0]) break; } } } if(k>i) return false; } return true; } public static void f(int i,int h){ if(i>4){ if(check(i-1)) sum++; } if(i>9) return; for(int j=1;j<=9;j++){ if(b[j]!=true){ if(i>1){ if(a[h][j]!=true&&a[j][h]!=true){ b[j] = true; d[i] = j; f(i+1,j); b[j] = false; }else if(b[(h+j)/2]){ d[i] = j; b[j] = true; f(i+1,j); b[j] = false; } }else{ d[i] = j; b[j] = true; f(i+1,j); b[j] = false; } } } } }
#include <bits/stdc++.h> using namespace std; int filter[10][10]; int stamp[9]; bool vis[10]; bool know[10][10]; int result,n; void dfs(int cot) { if(cot>=4) { int nKnow=0; for(int i=1;i<cot;i++) { if(know[stamp[i]][stamp[i-1]]) nKnow++; } if(n==nKnow) result++; } for(int i=1;i<=9;i++) { if(cot>0&&!vis[filter[stamp[cot-1]][i]]) continue; if(!vis[i]) { vis[i]=1; stamp[cot]=i; dfs(cot+1); vis[i]=0; } } return ; } int main() { memset(filter,0,sizeof(filter)); filter[1][3]=filter[3][1]=2; filter[4][6]=filter[6][4]=5; filter[7][9]=filter[9][7]=8; filter[1][7]=filter[7][1]=4; filter[2][8]=filter[8][2]=5; filter[3][9]=filter[9][3]=6; filter[1][9]=filter[9][1]=5; filter[3][7]=filter[7][3]=5; vis[0]=true; int ncase; result=0; scanf("%d",&n); memset(know,false,sizeof(know)); for(int i=0;i<n;i++) { int a,b; scanf("%d %d",&a,&b); know[a][b]=know[b][a]=true; } dfs(0); printf("%d\n",result); return 0; }
H:这道题目的意思实际上是给你一个无权图,然后让我们找核心站,那么这种题目一般是有并查集和搜索两种写法的,这里给出并查集思路,我们需要先用并查集检验【a,b】是否能联通,不能联通直接输出-1,结束程序。能联通,那么考虑除了a、b两站的其他站,其他站依次删除然后并查集寻找【a,b】的father,如果father相同,那么结果+1。所以枚举所有点即可(除了a,b两点),然后用并查集设置father,在最后再用并查集寻找k1,k2两点的father,如果相同,那么说明肯定联通。
public class Main { private static int[] father = new int[1001]; private static int n,m; private static int[][] ar; private static int t1,t2; public static void main(String[] args) { Scanner sn = new Scanner(System.in); n = sn.nextInt(); m = sn.nextInt(); ar = new int[1001][2]; for(int i = 0;i<m;i++){//表示第i个存储的两站连接点 ar[i][0] = sn.nextInt(); ar[i][1] = sn.nextInt(); } t1 = sn.nextInt(); t2 = sn.nextInt(); int count = 0; if(!isLinked()){System.out.println(-1);return;}//判断初试两点是否联通,不能联通直接结束程序 for(int i = 1;i<=n;i++){ if(i==t1||i==t2)continue;//去除需要检查联通的两点,这两点不需要遍历 for(int j = 1;j<=n;j++)father[j] = j;//初始化 for(int j = 0;j<m;j++){ if(ar[j][0]==i||ar[j][1]==i)continue;//去除的这个点所在的线段就不存在了 int a = findF(ar[j][0]); int b = findF(ar[j][1]); if(a>b){a^=b;b^=a;a^=b;} if(a!=b)father[b] = a; //以小的为父节点 } int a = findF(t1); int b = findF(t2); if(a!=b)count++;//如果共同父亲不同,那么肯定不连通,即那条被剪去的线路是核心线路,在这线路上有一头是核心站 } System.out.println(count); sn.close(); } public static boolean isLinked(){ for(int j = 1;j<=n;j++)father[j] = j; for(int j = 0;j<m;j++){ int a = findF(ar[j][0]); int b = findF(ar[j][1]); if(a>b){a^=b;b^=a;a^=b;} if(a!=b)father[b] = a; //以小的为父节点 } int a = findF(t1); int b = findF(t2); if(a==b)return true; return false; } private static int findF(int i) { if(father[i]==i)return i; return father[i] = findF(father[i]); } }
#include <bits/stdc++.h> using namespace std; int father[1001],n,m,ar[1001][2],t1,t2; int findF(int i) { if(father[i]==i)return i; return father[i] = findF(father[i]); } int isLinked(){ for(int j = 1;j<=n;j++)father[j] = j; for(int j = 0;j<m;j++){ int a = findF(ar[j][0]); int b = findF(ar[j][1]); if(a>b){a^=b;b^=a;a^=b;} if(a!=b)father[b] = a; //以小的为父节点 } int a = findF(t1); int b = findF(t2); if(a==b)return 1; return 0; } int main(){ scanf("%d%d",&n,&m); for(int i = 0;i<m;i++){//表示第i个存储的两站连接点 scanf("%d%d",*(ar+i),*(ar+i)+1); } scanf("%d%d",&t1,&t2); int count = 0; if(!isLinked()){printf("-1");return 0;}//判断初试两点是否联通,不能联通直接结束程序 for(int i = 1;i<=n;i++){ if(i==t1||i==t2)continue;//去除需要检查联通的两点,这两点不需要遍历 for(int j = 1;j<=n;j++)father[j] = j;//初始化 for(int j = 0;j<m;j++){ if(ar[j][0]==i||ar[j][1]==i)continue;//去除的这个点所在的线段就不存在了 int a = findF(ar[j][0]); int b = findF(ar[j][1]); if(a>b){a^=b;b^=a;a^=b;} if(a!=b)father[b] = a; //以小的为父节点 } int a = findF(t1); int b = findF(t2); if(a!=b)count++;//如果共同父亲不同,那么肯定不连通,即那条被剪去的线路是核心线路,在这线路上有一头是核心站 } printf("%d",count); return 0; }题解赶得比较匆忙,有很多细节的东西没有考虑到,也有一些代码没有手验,所以这份博客可能还会有所更新,另外,如果想要学习并查集以及搜索入门的同学可以看一下这两份视频:搜索入门 并查集 最后祝大家在蓝桥杯省赛中轻松省一,北京旅游!!!