蓝桥杯第 1 场算法双周赛 题解+AC代码

提醒:篇幅可能有点长,为了方便,大家可以直接看目录快速查找想要的内容

1:三带一【算法赛】 - 蓝桥云课 (lanqiao.cn)

题面:

intput:

No
Yes
No
Yes
Yes

output:

No
Yes
No
Yes
Yes

思路:

1.要满足一个三带一的话,就是任意选三个数相等且和另一个数不相等一个数不等,有4种情况,这里注意不能4个数都相等,不然都是炸了(斗地主中的炸),那么可以直接写判断,也可以排个序,压缩码量

2.排序后,分两种情况,前三个数相等且和最后一个数不等,或者后三个数相等且和第一个数不等

AC_code:java


import java.util.Arrays;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);

        String s;
        int T;  T= sc.nextInt();
        while(T-- >0){
            s=sc.next();

            char[] a=s.toCharArray();   //转化为字符数组

            Arrays.sort(a);

            if((a[0]==a[1]&&a[1]==a[2]&&a[2]!=a[3])||(a[3]==a[2]&&a[2]==a[1]&&a[1]!=a[0]))
                System.out.println("Yes");
            else System.out.println("No");
        }

    }
}

2:数树数【算法赛】 - 蓝桥云课 (lanqiao.cn)

题面:

intput:

3 6
R
L
LL
LR
RL
RR

output:

2
1
1
2
3
4

思路:递推/找规律

把二叉树放到一行当中,如下

1

1 2

1 2 3 4

1 2 3 4 5 6 7 8

1.当只有一层时,ans=1,当大于一层时,找找规律(ans为答案)

2.观察左右子树,每向下一层时,遇到L,ans=ans*2-1,遇到R,ans=ans*2

Ac_code:java


import java.util.Arrays;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt(),q=sc.nextInt();

        String s;
        while(q-->0){
            s=sc.next();
            int ans=1;
            for (int i = 0; i < s.length(); i++) {
                if(s.charAt(i)=='L')    ans=ans*2-1;
                else ans=ans*2;
            }
            System.out.println(ans);
        }


    }
}

3:分组【算法赛】 - 蓝桥云课 (lanqiao.cn)

题面:

input:

5 3
8 4 3 6 9

output:

1

思路:二分+贪心

1.怎么样才能让极差最小呢?贪心的想应该让身高相差小的分在一组,身高相差小那么有序的话就更好进一步思考,故先排一个序,排序后求极差值直接用最大值-最小值即可。

2.分成k组,那么要求出k个极差,让这k个极差的最大值最小,极差满足二段性,故用二分可以求

Ac_code:java

import java.util.Arrays;
import java.util.Scanner;

public class Main {

    static int k;
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        k=sc.nextInt();

        int []a=new int[n];
        for(int i=0;i<n;i++)    a[i]=sc.nextInt();
        Arrays.sort(a);

        int l=0,r=(int)1e9+1;   //二分极差
        while(l<r){
            int mid=(l+r)>>1;
            if(check(a,mid)) r=mid;
            else l=mid+1;
        }
        System.out.println(r-1);
    }

    //检查极差为mid是否满足条件
    private static boolean check(int[] a, int mid) {
        int cnt=1;  //记录分成几队
        for(int l=0,r=0;r<a.length;r++){
            if(a[r]-a[l]>=mid){ //注意这里>最后输出r,>=最后输出r-1
                l=r;    cnt++;
                if(cnt>k)   return false;
                    //分的队大于k,返回false,扩大极差l=mid+1
            }
        }
        return true;    //cnt小于等于k组  r=mid
    }
}

4:健身【算法赛】 - 蓝桥云课 (lanqiao.cn)

题面:

input:

10 3 3
1 4 9
0 3
1 7
2 20

output:

30

思路:完全背包

1.把空闲的时间区间段拆出来,看成一个小的背包容量

2.对拆出来的每个小背包,跑一个完全背包,累加每一个小背包的最大价值就是答案了

Ac_cod:java

import java.util.Scanner;

public class Main {
    static int n,m,q;
    static int N=(int)2e5+7;
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);

        n=sc.nextInt();
        m=sc.nextInt();
        q=sc.nextInt();

        int[] days=new int[q+2];    //纪录那些天不能去锻炼
        for (int i = 1; i <=q; i++) {
            days[i]=sc.nextInt();
        }

        int v[]=new int[m+1];
        int w[]=new int[m+1];

        for (int i = 1; i <=m; i++) {
            int t=sc.nextInt();
            v[i]=1<<t;  //体积,2^t
            w[i]=sc.nextInt();  //价值
        }

        days[q+1]=n+1;
        long []f=new long[N];
        long ans=0;
        for (int k = 1; k <=q+1; k++) {
            int V=days[k]-days[k-1]-1;  //计算当前小背包的体积

            //完全背包
            for (int i = 1; i <=m; i++) {
                for(int j=v[i];j<=V;j++)
                    f[j]=Math.max(f[j],f[j-v[i]]+w[i]);
            }
            ans+=f[V];  //累加每一个小背包的最大价值
        }

        System.out.println(ans);
    }
}

5:契合匹配【算法赛】 - 蓝桥云课 (lanqiao.cn)

题面:

input:

5
AbbCd
BcDaB

output:

Yes
2

思路:字符串哈希

1.两个齿轮都可以旋转,可以只看成一个齿轮可以旋转,让我们直接看s串,因为s是可以旋转的故可以直接拼接复制一倍s串(s+s),方便后续操作,

2.预处理出来与t匹配的串(大小写字母反转),记为target串,在s串中找到target串,找到了输出Yes,否则输出No

3.在s中找target串时,枚举起点记为i,如果匹配成功,注意是可以左右旋转的,可以把前1~i-1个字符移动后面,或者把i+1~n个字符移到前面,对两者取min(i-1,n-i)

4.匹配成功输出Yes并输出最小的旋转次数,否输出No

Ac_code:java

import java.util.Scanner;

public class Main {

    static int base=131;
    static int mod=(int)1e9+7;
    static int N=(int)2e6+7;

    static long[] hash1=new long[N];
    static long[] hash2=new long[N];
    static long[] p=new long[N];


    public static long get1(int l,int r){   //获得s串的hash值
        return ((hash1[r]-hash1[l-1]*p[r-l+1])%mod+mod)%mod;
    }

    public static long get2(int l,int r){   //获得与t匹配的hash值
        return ((hash2[r]-hash2[l-1]*p[r-l+1])%mod+mod)%mod;
    }

    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();

        String s=sc.next();
        String t=sc.next();

        //保证下标从1开始
        s=' '+s+s;  //可以旋转,复制一倍
        t=' '+t;
        char cht[]=t.toCharArray(); //t串转化为字符数组
        
        for(int i=1;i<=n;i++){ //将t串大写变小写,小写变大写
            if(cht[i]>='A'&&cht[i]<='Z') cht[i]+=32;
            else cht[i]-=32;
        }

        p[0]=1;
        for(int i=1;i<=n+n;i++){ //预处理hash值
            hash1[i]=(hash1[i-1]*base+s.charAt(i))%mod;
            if(i<=n) {
                hash2[i]=(hash2[i-1]*base+cht[i])%mod;
                p[i]=p[i-1]*base%mod;
            }
        }

        long target=get2(1,n); //获得与t匹配的串
        int ans=n;
        for(int i=1;i<=n;i++){
            if(target==get1(i,i+n-1))   //匹配成功,更新答案
                ans=Math.min(ans,Math.min(i-1,n-i+1));
        }       //将1~i-1个字符后移,或者i~n个字符前移,两者取min

        if(ans==n) System.out.println("No");
        else{
            System.out.println("Yes");
            System.out.println(ans);
        }
    }
}

6:奇怪的线段【算法赛】 - 蓝桥云课 (lanqiao.cn)

题面:

input:

4 3
1 3
2 5
3 7 
4 8
1 5
2 9
5 1

output:

1
2
3

思路:树状数组/线段树

简述题意:给你n个线段,m个询问,问包含a且不包含b的线段的数量有多少

对每个询问直接遍历一遍所有的线段是会tle的(时间复杂度n*q),考虑如何优化呢?这里对a,b分情况讨论

1.对a,b端点的大小分情况讨论,a==b时 答案为0

2.a<b时,从小到大遍历数轴上的点(此时a为左端点,b为右端点),遇到左端点就将左端点对应的所有右端点处+1,并计算此时右端点在[a,b-1]的个数,即为答案左端点为a,右端点为b的答案

3.a>b时,从大到小遍历数轴上的点(此时a为右端点,b为左端点),遇到右端点就将右端点对应的所有左端点处+1,并计算此时左端点在[b+1,a]的个数,即为右端点为a,左端点为b的答案

4.这题用到了,区间查询,单点修改,故直接上树状数组,当然线段树也行,还有注意java要快读,cpp用scanf就行

Ac_code:java

import java.io.*;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Scanner;

import static java.awt.AWTEventMulticaster.add;

public class Main {
    static int N=(int)2e5+7;
    static int tr[]=new int[N];

    public static void add(int idx,int val){
        for(int i=idx;i<N;i+=i&-i)  tr[i]+=val;
    }

    public static int sum(int n){
        int res=0;
        for(int i=n;i>0;i-=i&-i)    res+=tr[i];
        return res;
    }

    //java这题用Scanner会超时,故写了一个快读
    public static void main(String[] args) throws IOException {
        Scanner sc=new Scanner(System.in);

        int n=nextInt(),m=nextInt();

        //l[R]:右端点为R对应的的所有左端点
        //r[L]:左端点为L对应的所有的右端点
        //q[a]:端点a里存的是端点b,与第id个询问
        ArrayList<Integer>[] l=new ArrayList[N];
        ArrayList<Integer>[] r=new ArrayList[N];
        ArrayList<Node>[] q=new ArrayList[N];

        for(int i=0;i<N;i++){
            l[i]=new ArrayList<Integer>();
            r[i]=new ArrayList<Integer>();
            q[i]=new ArrayList<>();
        }

        for(int i=1;i<=n;i++){
            int L=nextInt(),R=nextInt();
            l[R].add(L);
            r[L].add(R);
        }

        for(int i=0;i<m;i++){
            int a=nextInt(),b=nextInt();
            q[a].add(new Node(b,i));
        }

        int[] ans=new int[m];

        //a<b
        //枚举左端点a,记录右端点在[a,b-1]中的数量
        for(int a=1;a<N;a++){
            for(int R:r[a]) add(R,1);
            for(Node p:q[a])
                if(p.b>a)   //左端点为a,右端点为b
                    ans[p.id]=sum(p.b-1)-sum(a-1);
        }

        //记得清0
        for(int i=0;i<N;i++)    tr[i]=0;

        //a>b
        //枚举右端点a,记录左端点在[b+1,a]中的数量
        for(int a=N-1;a>=1;a--){
            for(int L:l[a]) add(L,1);
            for(Node p:q[a])
                if(p.b<a)   //左端点为b,右端点为a
                    ans[p.id]=sum(a)-sum(p.b);
        }

        for(int i=0;i<m;i++) {
            pw.println(ans[i]);
            pw.flush(); //每次输出都要执行
        }
    }

    //读字符串时空格,回车,换行都进行分割
    static StreamTokenizer st = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));

    //pw.println(),没写一次记得刷新缓存区pw.flush()
    static PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));

    public static double nextDouble() throws IOException{ st.nextToken(); return st.nval; }
    public static float nextFloat() throws IOException{ st.nextToken(); return (float)st.nval; }
    public static int nextInt() throws IOException { st.nextToken(); return (int)st.nval; }
    public static String next() throws IOException{ st.nextToken(); return st.sval;}
}

class Node{
    int b,id;

    public Node(int b, int id) {
        this.b = b;
        this.id = id;
    }
}

双周赛题解将持续更新,希望对你有用!

描述有误,欢迎指正,

  • 9
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值