今天的比赛题目是今年蓝桥杯的模拟赛原题,所以题解大概是可以在网上给搜索到的,但是希望同学们都是本着一个公平公正的态度来打这一场比赛的,这一场比赛大概前两题做出来,代码填空做对,编程大题大概一题半就可以拿到省一了,因为这次蓝桥杯官方的模拟赛,比赛连接在这里​​蓝桥杯模拟赛​


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;
}

题解赶得比较匆忙,有很多细节的东西没有考虑到,也有一些代码没有手验,所以这份博客可能还会有所更新,另外,如果想要学习并查集以及搜索入门的同学可以看一下这两份视频:​​搜索入门​​​ ​​并查集​​ 最后祝大家在蓝桥杯省赛中轻松省一,北京旅游!!!