SWPU-ACM集训队周赛之个人赛(4-3,蓝桥杯模拟赛)----题解

来源:互联网 发布:太原淘宝电动三轮价 编辑:程序博客网 时间:2024/05/24 04:36

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


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

1 0
原创粉丝点击