第十届蓝桥杯软件类省赛 Java 大学 B 组 题目以及详细解析

蓝桥杯 2020年省赛真题 (Java 大学B组) 蓝桥杯 2020 年省赛真题 (Java 大学 B 组 ) 阅读详情


包含全部题目和解析以及AC代码,并确保代码正确性。

给参加蓝桥杯的小伙伴们推荐个OJ: New Online Judge
这个OJ的题库里有蓝桥杯历年的省赛和决赛真题,能亲眼看到AC才能确保代码正确性。

结果填空题

A

在这里插入图片描述在这里插入图片描述
答案:
97 + 99 + 99 + 97 + 98 = 490 选法不唯一, 和唯一。

找出每个位置评分最高的,并且每个人只能担任一个号位。

因为数据很少,直接手算就可以,但是如果这道题是编程题,数据量很大,好像不太好处理。

B

在这里插入图片描述
按子串长度从(1~n)对原字符串进行截取,并判断该字符串是否出现过,没有就把答案加一。

做法是可以创建一个set集合,将所有子串加入进去,最后集合中字符串的数量就是答案。

代码:

import java.util.*;
import java.io.*;

public class Main{
    public static void main(String[] args){
        Scanner in = new Scanner(System.in);
        
        HashSet<String> res = new HashSet<String>();
        String s = in.nextLine();
        int n = s.length();
        for(int i = 0; i < n; i++){
            for(int j = i+1; j <= n; j++){
                res.add(s.substring(i, j));
            }
        }
        
        System.out.println(res.size());
    }
}

答案:100

C

在这里插入图片描述
这道题就是由斐波那契数列变形而来的,但是题目要的是第20190324项的后四位数字,如果真的求出第20190324项,在取出四位,用大数类也算不出来,因为数太大了。
然而仔细一想,其实我们计算的只需要保留每一项的后四位就行了,因为其他位影响不到最终答案。 因为每一项都由前三项加和得来,所以计算过程中只需要用四个变量保留前三项和答案。

	import java.util.*;
import java.io.*;

public class Main{
    public static void main(String[] args){
        int first = 1, second = 1, third = 1;
        int res = 0;
        for(int i = 4; i <= 20190324; i++){
            res = (first + second + third)%10000;
            first = second;
            second = third;
            third = res;
        }
        System.out.println(res);
    }
}

答案:4659

D

在这里插入图片描述
从小到大枚举三个正整数i,j,l,并且 a <= b <= c,这是为了防止计算重复。
在枚举的过程中判断每个数是不是包含2和4. 最后判断a+b+c是不是等于2019.

import java.util.*;
import java.io.*;

public class Main{
   public static Boolean find(int x){
	   while(x != 0){
		  if(x%10 == 2 || x%10 == 4){
			  return true;
		  }
		  x /= 10;
	   }
	   return false;
   }
	
	public static void main(String[] args) throws IOException{
		File file = new File("E:\\output.txt");
		
		if(file.exists())
			file.createNewFile();				//如果指定文件不存在,新建文件
				
			BufferedWriter out = new BufferedWriter(new FileWriter(file));
	    	long sum = 0;
	        for(int i = 1; i <= 2019; i++)
	        {
	        	if(find(i))	continue;
	        		
	            for(int j = i+1; j + i <= 2019; j++)
	            {
	            	if(find(j)) continue;
	                
	            	for(int k = j+1; k + j <= 2019; k++)
	                {
	            		if(find(k)) continue;
	            		
	            		if(i + j + k == 2019){
	                        sum++;
	                        out.write(i + " " + j + "  " + k+ "\n");
	                    }
	                }
	            }
	        }
	        
	        out.close();
	        System.out.print(sum);
	}
}


为了方便查看答案的正确性, 将所有组合输出到output文件中。
答案 :40785

E

在这里插入图片描述
迷宫:


01010101001011001001010110010110100100001000101010
00001000100000101010010000100000001001100110100101
01111011010010001000001101001011100011000000010000
01000000001010100011010000101000001010101011001011
00011111000000101000010010100010100000101100000000
11001000110101000010101100011010011010101011110111
00011011010101001001001010000001000101001110000000
10100000101000100110101010111110011000010000111010
00111000001010100001100010000001000101001100001001
11000110100001110010001001010101010101010001101000
00010000100100000101001010101110100010101010000101
11100100101001001000010000010101010100100100010100
00000010000000101011001111010001100000101010100011
10101010011100001000011000010110011110110100001000
10101010100001101010100101000010100000111011101001
10000000101100010000101100101101001011100000000100
10101001000000010100100001000100000100011110101001
00101001010101101001010100011010101101110000110101
11001010000100001100000010100101000001000111000010
00001000110000110101101000000100101001001000011101
10100101000101000000001110110010110101101010100001
00101000010000110101010000100010001001000100010101
10100001000110010001000010101001010101011111010010
00000100101000000110010100101001000001000000000010
11010000001001110111001001000011101001011011101000
00000110100010001000100000001000011101000000110011
10101000101000100010001111100010101001010000001000
10000010100101001010110000000100101010001011101000
00111100001000010000000110111000000001000000001011
10000001100111010111010001000110111010101101111000


思路:
这道题就是BFS求最短路径,然后题目要求的是最短路径的走法,DULR分别表示下上左右,并且在步数相等的情况下,要求字典序最小。

  1. 首先解决字典序最小的问题,字典序 D < < < L < < < R < < < U
    所以为了使字典序最小,我们要按照D L R U的先后顺寻来搜索。

  2. 搜索的时候用一个数组记录一下当前点是由哪个点转移过来的。
    最后从出口倒推到入口就可以了。

  3. 因为是地图是二维的,所以要保存上一个点的横坐标和纵坐标,可以写一个pair类,并创建为二维数组,例如:pre[nx][ny].x = i, pre[nx][ny].y = j表示点(nx, ny)由(i, j)走过来。

    或者直接开个三维数组,第三维大小开2,分别表示横坐标和纵坐标。
    例如:pre[nx][ny][0] = i, pre[nx][ny][1] = j表示点(nx, ny)由(i, j)走过来。

代码:

import java.io.*;
import java.util.*;

public class Main{
    static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    static BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
    
    static final int N = 1005;
    static int n, m;
    static int[][] g = new int[N][N], st = new int[N][N], q = new int[N*N][2];
    static int[][][] pre = new int[N][N][2];
    
    public static int Int(String s){
        return Integer.parseInt(s);
    }
    
    public static void bfs(int i, int j) throws IOException{
        int[] dx = {1, 0, 0, -1}; // 下 左 右 上
        int[] dy = {0, -1, 1, 0};
        
        int h = 0, t = 0;
        q[t][0] = i; q[t++][1] = j;
        
        st[i][j] = 1;
        while(h != t){
            int x = q[h][0], y = q[h++][1];
            
            for(int l = 0; l < 4; l++){
                int nx = x + dx[l];
                int ny = y + dy[l];
                 
                if(nx < 0 || nx >= n || ny < 0 || ny >= m) continue;
                if(g[nx][ny] == 1) continue;             
                if(st[nx][ny] == 1) continue;

                q[t][0] = nx; q[t++][1] = ny;
                st[nx][ny] = 1;
                
                pre[nx][ny][0] = x; pre[nx][ny][1] = y;
                if(nx == n-1 && ny == m-1) return ;
            }
        }
    }
    
    public static void main(String[] args) throws Exception{
        String[] ss = in.readLine().split(" ");
        n = Int(ss[0]);
        m = Int(ss[1]);
        
        for(int i = 0; i < n; i++){
            String s = in.readLine();
            
            for(int j = 0; j < s.length(); j++)
                g[i][j] = Int(String.valueOf(s.charAt(j)));
        }
       
        bfs(0, 0);      
        
        int i = n-1, j = m-1; // 从出口回溯到入口。       
        String res = "";
        
        while(i != 0 || j != 0){
      
            if(i - pre[i][j][0] == 0){ // x不变,说明是向左或者向右。
                if(j - pre[i][j][1] == 1) // 3 2 -> 3 3 说明上一个点是通过向右走到达(i,j)
                    res = "R" + res;
                else
                    res = "L" + res; // 向左
            }
            else{
                if(i - pre[i][j][0] == 1) // 2 3 -> 3 3 说明上一个点是通过向下走到达(i,j)
                    res = "D" + res;
                else
                    res = "U" + res; // 向上
            }
            
            int t = i;
            i = pre[i][j][0];
            j = pre[t][j][1];
        }
        out.write(res);
        out.flush();
    }
}

答案:

DDDDRRURRRRRRDRRRRDDDLDDRDDDDDDDDDDDDRDDRRRURRUURRDDDDRDRRRRRRDRRURRDDDRRRRUURUUUUUUULULLUUUURRRRUULLLUUUULLUUULUURRURRURURRRDDRRRRRDDRRDDLLLDDRRDDRDDLDDDLLDDLLLDLDDDLDDRRRRRRRRRDDDDDDRR

程序设计题

F

在这里插入图片描述
题目链接: https://www.acwing.com/problem/content/description/1247/

题意:求出1~n中包含2, 0, 1, 9这几个的数的数字之和。

数据规模是10000, 所以可以直接枚举1~n中所有的数,判断其是否包含2,0,1,9其中的一个,有就加上。 时间复杂度最高是 O ( 5 ∗ n ) O(5*n) O(5∗n);完全不用担心超时。

AC代码:

import java.io.*;
import java.util.*;
 
public class Main{     
    public static void main(String[] args) throws Exception{
        
    	Scanner in = new Scanner(System.in);
    	int n = in.nextInt();
         
        int res = 0;
        for(int i = 1; i <= n; i++){
            int x = i;
            while(x != 0){
                int r = x % 10;
                if(r == 0 || r == 1 || r == 2 || r == 9){
                    res += i;
                    break;
                }
                x /= 10;
            }    
        }
         
       System.out.print(res);
    }
}

G

在这里插入图片描述
在这里插入图片描述在这里插入图片描述
题目链接:http://oj.ecustacm.cn/problem.php?id=1458
题意:

有N家店,每家店都有一个优先级,初始时刻都为0,对于任意时刻,如果某家店接到订单,则优先级+2,如果没有则-1,最低为0。 优先级大于5,则加入优先缓存,小于等于3则移除,给出T时刻以内的M条订餐信息,求第T时刻谁在优先缓存中。

思路:

这道题是一个模拟题。我们根据M条信息模拟每家店的优先级加减情况。最后判断一下哪些店位于优先缓存。

数据规模最大是 1 0 5 10^5 105,所以该题的时间复杂度必须小于 o ( n 2 ) o(n^2) o(n2)。

如果我们枚举从1到T的每个时刻,然后判断每个时刻有哪些店接到了订单,哪些店没有接到订单,因为T和N都是 1 0 5 10^5 105,所以这样的时间复杂度是 O ( n 2 ) O( n^2) O(n2)会超时,所以我们换种方式枚举。

因为只有M条信息中出现的店才接到了订单,所以我们只需要考虑有订单的店,因为其他的店的优先级一定都是0.

然后我们可以用这M条信息模拟接到订单的店的优先级的加减情况。
第一步:排序
我们可以先对M条信息按时刻从小到大进行排序,如果时刻相同就按店号从小到大排序。时间复杂度为 O ( n ∗ l o g n ) O(n*logn) O(n∗logn),满足要求。

得到了时刻和店号都为升序的订单信息后:
第二步:处理优先级

我们从前到后依次处理每条信息时, 记录一下每家店上次是什么时刻接到订单的, 这样我们就可以算出每家店这次接到订单前,有多少个时刻没有订单,然后将优先级减去多少,如果其优先级小于等于3,就标记为fasle,表示其不在优先缓存,如果减成了负数, 就赋值为0.然后加上这次接到订单的2优先级, 加完后判断其优先级是不是大于5,如果是标记为true,表面将其加入优先缓存中。

第三步:计算接到过订单的店,从最后时刻到T时刻的优先级减了多少。

因为处理完M条信息后,可能存在某些店的优先级大于6,但它在T时刻没有订单,所以我们最后额外处理一下M条信息中的所有店到T时刻后的优先级是多少。
直接用T时刻减去每家店的最后接单时刻,就得到了该点有多少个时刻没有订单。然后优先级相应的减去多少。然后判断该店还在不在优先缓存中。
如果在,答案数就加一。

AC代码:

import java.io.*;
import java.util.*;
 
public class Main{
    static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    static BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
     
    static int[][] arr = new int[100005][2];
    static int[] score = new int[100005];
    static int[] st = new int[100005];
    public static int Int(String s){
        return Integer.parseInt(s);
    }
     
    public static void main(String[] args) throws Exception{
        String[] s = in.readLine().split(" ");
        int N = Int(s[0]);
        int M = Int(s[1]);
        int T = Int(s[2]);
         
        for(int i = 0; i < M; i++){
            String[] ss = in.readLine().split(" ");
            arr[i][0] = Int(ss[0]);
            arr[i][1] = Int(ss[1]);
        }
         
        Arrays.sort(arr, 0, M, new Comparator<int[]>(){
            public int compare(int[] a, int[] b){
                if(a[0] == b[0]) return a[1] - b[1];
                return a[0] - b[0];
            }
        });
         
        int[] last = new int[100005];
        TreeSet<Integer> set = new TreeSet<>(); 
        for(int i = 0; i < M && arr[i][0] <= T; i++){// 处理M条信息,记录每家店的最后接单时间。初始为0
         
            int time = arr[i][0]; 
            int id = arr[i][1];
            set.add(id);// 接到过订单的店
              
            if(time - last[id] > 1)
                   score[id] -=  (time - last[id] - 1);
            if(score[id] < 0) score[id] = 0;
            if(score[id] <= 3) st[id] = 0;  // 要先置为false 否则如果先降到了3再加上2
                                            // 虽然大于3,但不大于5
            score[id] += 2;
                 
            if(score[id] > 5) st[id] = 1;
            
            last[id] = time; // 记录某家店的最后接单时刻。
            //out.write(score[id] + "\n");
        }
         
        int num = 0;
        for(int i : set){
            //out.write("s: " + score[i] + " last: "+ last[i] + "  T: "+ T + "\n");
            if(last[i] < T)
                score[i] -= (T - last[i]);
             
            if(score[i] <= 3)
                st[i] = 0;
            if(st[i] == 1) num ++;
        }
         
        out.write(num +"");
        out.flush();
    }
}

H

在这里插入图片描述在这里插入图片描述
题目链接:http://oj.ecustacm.cn/problem.php?id=1473

这道题就是找出给定区间内Alice和Bob的对数。

我们可以先预处理出来所有的Alice和Bob的位置,然后两个for循环,暴力枚举每个Alice的合法范围内有多个Bob, 但是,因为字符串的长度是一百万,所以这个时间复杂度肯定是会超时的。

那么我们如何进行优化?

我们从前到后依次找到的每个Alice和Bob,其在数组中的位置一定是递增的,那么用for循环挨个找不是太**了?《硅谷》中的男主就因为使用for循环暴力查找有序的序列而被大家无情嘲讽。所以为了不被大佬们嘲讽,我们还是用二分吧, 我们可以分别二分出在合法范围内,Alice最左边的Bob的位置 L 和Alice最右边的Bob的位置 R。然后 R - L + 1就是当前的Alice和Bob同时出现的次数,枚举每个Alice就能得到最终答案。

时间复杂度是 O ( n ∗ l o g n ) O(n*logn) O(n∗logn) ,稳过不T。

另外,因为处理出来的Alice和Bob的位置都是升序的,我们也可以用双指针算法进行优化,也就是维护一个动态区间,左区间端点是L, 右区间端点是R,区间内是对于当前位置的Alice而言所有合法的Bob。

先找到一个距离当前Alice右边最远的Bob也就是找到R,然后找到距离当前Alice左边最远的Bob,也就是找到L,累加答案后,遍历下一个位置的Alice,重新维护这个区间。

这里其实和二分的思想是类似的,只不过二分是每次都重新找,而双指针是动态的维护区间左右端点。详见代码。

预处理 + 二分:

import java.io.*;
import java.util.*;
import java.math.*;
   
public class Main{
    static BufferedReader in = new BufferedReader(new InputStreamReader(System.in),32768);
    static BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
      
    static final int N = 1000010;
    static String s;
    static int[] A = new int[N], B = new int[N];
    static int n, cnta, cntb;
      
    public static boolean chack(int i){
        if(i < 0 || i >= n)
            return false; 
        char c = s.charAt(i);
        return c >= 'a' && c <= 'Z' || c >= 'A' && c <= 'Z';
    }
       
    public static int findl(int x) throws IOException{ // 大于等于 x 的最小值
        int l = 0, r = cntb - 1;
        while(l < r){
            int mid = l + r>> 1;
             
            if(B[mid] >= x) r = mid;
            else l = mid + 1;
        }
        return l;
    }
     
    public static int findr(int x) throws IOException{
        int l = 0, r = cntb - 1;
        while(l < r){
            int mid = l + r + 1 >> 1;
            if(B[mid] <= x) l = mid;
            else r = mid - 1;
        }
        return l;
    }
     
    public static void main(String[] args) throws Exception{
        int k = Integer.parseInt(in.readLine());
        s = in.readLine();
         
        n = s.length();
        for(int i = 0; i < n; i++){
            if(!chack(i-1)){
                if(i + 4 < n && s.substring(i, i + 5).equals("Alice") && !chack(i+5))
                    A[cnta ++] = i;  
                else if(i + 2 < n && s.substring(i, i + 3).equals("Bob") && !chack(i+3))
                    B[cntb ++] = i;
            }
        }
          
        long res = 0;
        for(int i = 0; i < cnta; i++){
            int l = findl(A[i] - k - 3); // 找到在合法范围内,Alice最左边的Bob
             
            if(cntb == 0 || B[l] > A[i] + k + 5){ //没找到。
                continue;
            } 
             
            int r = findr(A[i] + k + 5); // 找到合法范围内,Alice最右边的Bob // 1  7 
             
            res += r - l + 1;
        }
        out.write(res + "");
        out.flush();
    }
}

预处理 + 双指针:

import java.io.*;
import java.util.*;
import java.math.*;
  
public class Main{
    static BufferedReader in = new BufferedReader(new InputStreamReader(System.in),32768);
    static BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
     
    static final int N = 1000010;
    static String s;
    static int[] A = new int[N], B = new int[N];
    static int n;
     
    public static boolean chack(int i){
        if(i < 0 || i >= n)
            return false; 
        char c = s.charAt(i);
        return c >= 'a' && c <= 'Z' || c >= 'A' && c <= 'Z';
    }
      
    public static void main(String[] args) throws Exception{
        int k = Integer.parseInt(in.readLine());
        s = in.readLine();
        
        n = s.length();
        int cnta = 0, cntb = 0;
        for(int i = 0; i < n; i++){
            if(!chack(i-1)){ // 注意不要用 == 判断是否相等。
                if(i + 4 < n && s.substring(i, i + 5).equals("Alice") && !chack(i+5))
                    A[cnta ++] = i;  
                else if(i + 2 < n && s.substring(i, i + 3).equals("Bob") && !chack(i+3))
                    B[cntb ++] = i;
            }
        }
         
      
        int l = 0, r = -1;   
        long res = 0;
        if(cntb == 0 || cnta == 0) res = 0; 
        else
            for(int i = 0; i < cnta; i++){
                // 先判断r+1是不是在范围内, 再 r++;
                while(r + 1 < cntb && B[r + 1] <= A[i] + 5 + k)
                	r++;
                // 找到第一个在合法区间内的Bob
                while(l <= r && B[l] + 3 + k < A[i])
                	l++;
                	
                res += r - l + 1;
            }
        out.write(res + "");
        out.flush();
    }
}

I

在这里插入图片描述在这里插入图片描述
题目链接:
https://www.acwing.com/problem/content/1249/

这道题看着很简单,我刚开始做的时候想当然的将数组从大到小排序,然后加上n个最大的数之后,减去剩下的数。

但是这种思路是错的

比如数据:
1 3
5 4 3 2 1

如果按照刚才的思路,答案是5 + 4 - 3 - 2 - 1 = 3

而正确答案是 5 + 4 - (1 - 2 - 3) = 13

对应的一种后缀表达式为:5 4 + 1 2 - 3 - -

因为题目是让我们自己构造后缀表达式,也就是说我们可以随便改变n+m+1个数的运算顺序,即我们可以在对应的中缀表达式上任意加括号。

有n + m +1 个数,n个加号,m个减号。

如果只有加减操作,则k个数的最大值一定是k个数相加。所以说,我们要尽量使负号变为正号,即尽量减少减号的个数。

如果不存在负数,我们必须要减去正数。
因为负负得正,所以为了使结果最大,我们可以用一个减号使其他m-1个减号变为正号。即我们要构造出这样的中缀表达式:

A + B - (C - D - E - F - G)

也就是:
A + B + D + E + F + G - C

即只需要减去一个最小的数。

如果存在负数,我们直接减去负数,使其变为正数。如果减号不够,就将其加入括号中。

所以最后无论怎么样都可以构成这样的式子:

A + B + D + E + F + G - C

但是只有一个数的正负不能被改变,就是A,因为只要有减号,就不能没有被减数。为了使结果最大,A要确保是最大数。

代码:

import java.io.*;
import java.math.*;

public class Main{
    static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    static BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
    
    public static int Int(String s){
        return Integer.parseInt(s);
    }
    
    public static void main(String[] args) throws Exception{
        String[] s = in.readLine().split(" ");
        int n = Integer.parseInt(s[0]);
        int m = Integer.parseInt(s[1]);
        
        String[] arr = in.readLine().split(" ");
        
        int len = n + m + 1;
        long sum = 0;
        if(m == 0){ // 全是加号
            for(int i = 0; i < len; i++)
                sum += Integer.parseInt(arr[i]);
        }
        else{
            int max = 0, min = 0;
            
            for(int i = 0; i < len; i++){
                if(Int(arr[min]) > Int(arr[i]))
                    min = i;
                if(Int(arr[max]) < Int(arr[i]))
                    max = i;
            }
            
            sum = Int(arr[max]) - Int(arr[min]);
            
            for(int i = 0; i < len; i++){
                if(i != min && i != max)
                    sum += Math.abs(Int(arr[i]));
            }

        }
        out.write(sum + "");
        out.flush();
    }
    
}

J

在这里插入图片描述
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述
参考yxc题解。视频讲解:https://www.bilibili.com/video/BV1Mb411t7M1

我这里大致说一下思路:

由题目中给的操作我们可以发现,对一个 a i a_i ai​ 进行一次灵能传输,对其前缀和的影响是交换了 S i 和 S i − 1 的 位 置 S_i 和 S_{i-1}的位置 Si​和Si−1​的位置。

即一次灵能传输对前缀和的影响是:

( S i − 1 , S i , S i + 1 ) (S_{i-1},S_i ,S{i+1}) (Si−1​,Si​,Si+1) — > ( S i , S i − 1 , S i + 1 ) (S_{i},S_{i-1} ,S{i+1}) (Si​,Si−1​,Si+1)

我们要求的就是通过交换 [ S 1 . . . S n − 1 ] [S_1 ...S_n-1] [S1​...Sn​−1]使得 ∣ S 1 − 0 ∣ ∣ S 2 − S 1 ∣ . . . ∣ S n − S n − 1 ∣ |S_1 - 0| |S_2 - S_1|...|S_n - S_{n-1}| ∣S1​−0∣∣S2​−S1​∣...∣Sn​−Sn−1​∣的中的最大值达到最小。注意 S n S_n Sn​ 的位置不能变,因为无论怎么操作, S n S_n Sn​ 的值始终不变。又因为 S 1 S_1 S1​ 的值一定会减去0,所以我们直接将 S 0 设 为 0 S_0 设为 0 S0​设为0 ,并且 S 0 和 S n S_0 和 S_n S0​和Sn​的位置是不能变的。

如果排完序后,0是最小值, S n S_n Sn​是最大值,那么我们直接遍历整个前缀和数组,得到的最大差值的绝对值,就是答案。因为在此种前缀和的排列方式中,无法找到另一种排列方式使得两个前缀和之间的差值变的更小。

比如 序列 0 1 2 3 4 5 6

改变其中任意两个数的位置,都会使差值变大。

但是如果有负数,或者{S_n}不是最大值,直接排序就会出错。

比如 序列 : -3 -2 -1 0 2 3 5, S 0 = 0 , S n = 2 S_0 = 0, S_{n} = 2 S0​=0,Sn​=2

正确的排列方式:0 -2 -3 -1 3 5 2

答案为 4

为什么这样排列呢, 因为 0 要先 到达最小值 然后由最小值到达最大值,再由最大值到达 S n S_n Sn​是最优的, 如果 0 下一个数排成比0大的数,那么该数后面的差值就会更大。

然后为了使从0到最小值和从最小值到最大值的差值尽可能的小,我们不能直接将所有小于0大于最小值的的数全都排到它俩中间,比如0 -1 -2 -3 2 3 5
这样排到正数的时候就会使差值更大,为了缓冲最小值到达正数的部分,
我们要隔一个数排,比如 0 之后 排-2 而不排-1 ,-1 留作缓冲最小值和正数部分。

如果隔两个数排,0 和 第三个数的差值又变大了。所以最优情况就是将前缀和序列排好序后, 从 0 往前取数, 每隔一个取一个, 从Sn往后取数,每隔一个取一个,然后再中间填上剩余的数。之后遍历整个排列后的序列,求最大差值的绝对值就是答案。

想不明白隔一个取一个的可以手动模拟一下数据。

代码:

import java.io.*;
import java.math.*;
import java.util.*;

public class Main{
    static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    static BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
    
    static final int N = 300010;
    
    static long[] S = new long[N];
    static int[] st = new int[N];
    
    public static long Int(String s){
        return Long.parseLong(s);
    }
    
    public static void main(String[] args) throws IOException{
        int T = Integer.parseInt(in.readLine());
        
        while(T != 0){
            T --;
            int n = Integer.parseInt(in.readLine());
            
            String[] s = in.readLine().split(" ");
            
            S[0] = 0;
            
            for(int i = 1; i <= n; i++){
                S[i] = Int(s[i-1]); // 得到前缀和数组
                S[i] += S[i-1];
            }
            
            long s0 = 0, sn = S[n]; // 记录s0 和 sn 的值,以便排序后找到他们的位置
            
            if(s0 > sn){ // 对于sn < 0的情况,我们将其值调换,仍作为s0是最小值 sn是最大值处理。
                long temp = s0;
                s0 = sn;
                sn = temp;
            }
            
            Arrays.sort(S, 0, n + 1);
            
            for(int i = 0; i <= n; i++){
                if(s0 == S[i]){
                    s0 = i;
                    break;
                }
            }
            
            for(int i = 0; i <= n; i++){
                if(sn == S[i]){
                    sn = i;
                    break;
                }
            }
            
            long[] a = new long[N];
            
            Arrays.fill(st, 0);
            int l = 0, r = n;
            // 隔一个取一个
            for(int i = (int) s0; i >= 0; i -= 2){
                a[l ++] = S[i];
                st[i] = 1;
            }
                
            for(int i = (int) sn; i <= n; i += 2){
                a[r --] = S[i];
                st[i] = 1;
            }
                
            for(int i = 0; i <= n; i++){
                if(st[i] == 0)
                    a[l ++] = S[i];
            }
            
            // 查找答案
            long res = 0;
            for(int i = 1; i <= n; i++){
                res = Math.max(res, Math.abs(a[i] - a[i-1]));    
            }
            
            out.write(res + "\n");
            out.flush();
        }
    }
}
蓝桥杯个人赛软件类历届真题及其解析 阅读详情

相关推荐

第十二届蓝桥杯 2021年省赛真题 (C/C++ 大学B组)

蓝桥杯 2021年省赛真题 (C/C++ 大学B组 )

NOW I GOT ONE WAY 2万+

2018第九届蓝桥杯JavaB组省赛真题及详解

2018第九届蓝桥杯JavaB组省赛真题及详解 第一题:第几天 题目描述 2000年的1月1日,是那一年的第1天。 那么,2000年的5月4日,是那一年的第几天? 注意:需要提交的是一个整数,不要填写任何多余内容。 直接数,125 第二题:方格计数 题目描述 如图所示,在二维平面上有无数个1x1的小方格。 我们以某个小方格的一个顶点为圆心画一个半径为1000的圆。 你能计算出这个圆里有多少个完整...

hyx153183740的博客 8095

2020年第十一届蓝桥杯省赛第二场Java大学B组真题及解析

总结:与往届题目相比,,暴力题型少了许多,增加了dp和图论的考察,不过在不会的情况下,暴力还是能骗不少分的 目录 试题A:门牌制作 试题B:寻找2020 试题C:蛇形填数 试题D:七段码 试题E:排序 试题F:成绩分析 试题G:单词分析 试题H:数字三角形 试题I:子串分值和 试题J:装饰珠 试题A:门牌制作 思路:枚举就行,水题一道,没啥说的,上代码 public class 门牌制作 { public static void main(String[] args

imreal_的博客 8775

第十届蓝桥杯Java b组省赛真题解析(附有详细代码,个人题解)

为了大家可以更清楚地看到题目,我特意去找了当时的比赛文档,希望大家可以有更好的体验。 试题A:组队 组队这一题是签到题目,很简单这里不在赘述。 答案:490(填空题,直接写答案即可) 试题B:不同字串 代码如下: import java.util.*; /* 第十届第二题:不同字串 */ public class 不同字串{ public static void main(String[] args) { Scanner s...

m0_58941767的博客 2721

2023年十四届蓝桥杯省赛大学B组真题(Java完整版)

2023年十四届蓝桥杯省赛大学B组真题完整题解

2302_76207485的博客 8620

第十五届蓝桥杯大赛软件赛省赛C/C++ 大学 B 组

第十五届的题目在规定时间内做出了前5道,还有2道找时间再磨一磨。

神探阿航的博客 9419

第十届蓝桥杯省赛java大学b组题目及解析

一、填空题 试题A:组队 简单题,不做过多解析,直接上代码 枚举代码: import java.util.Scanner; public class A{ static int ans=0,sum=0; static int[][] a= {{0,0,0,0,0,0},{0,97,90,0,0,0},{0,92,85,96,0,0},{0,0,0,0,0,93},{0,0,0,0,80,86}, {0,89,83,97,0,0},{0,82,86,0,0,0},{0,0,0,0,87,90

m0_46669582的博客 493

第十一届蓝桥杯大赛软件类省赛Java大学B组(第一场)

第十一届蓝桥杯大赛软件类省赛Java大学B组(第一场) YeRikGSunlRzgDlvRwYkXkrGWWhXaA import java.util.Date; import java.text.ParseException; import java.text.SimpleDateFormat; public class Main { public static void main(String[] args) throws ParseException { SimpleDateFor

HelloACM_ICPC的博客 3324

第十届蓝桥杯大赛个人赛省赛(软件类)真题-Java语言B组

目录 1.组队 2.不同子串 3.数列求值 1.组队 【问题描述】 作为篮球队教练,你需要从以下名单中选出 1 号位至 5 号位各一名球员, 组成球队的首发阵容。 每位球员担任 1 号位至 5 号位时的评分如下表所示。请你计算首发阵容 1 号位至 5 号位的评分之和最大可能是多少? (如果你把以上文字复制到文本文件中,请务必检查复制的内容是否与文 档中的一致。在试题目录下有一个文件 team.txt,内容与上面表格中的相同, 请注意第一列是编号) 【答案提交】   这是一道结果填空的题,

WHT869706733的博客 991

2021年 第12届 蓝桥杯 Java B组 省赛真题详解及小结【第1场省赛 2021.04.18】

2021年 第12届 蓝桥杯 Java B组 省赛真题详解及小结【第1场省赛 2021.04.18】

upward 9306

Dynamics 365 F&O新手必看:Visual Studio 2019搭建项目框架全流程(含Model避坑指南)

本文详细介绍了如何在Visual Studio 2019中为Dynamics 365 Finance and Operations搭建企业级项目框架,包括Model分层策略、Solution管理及开发工作流优化。特别提供了Model避坑指南和实用配置建议,帮助新手快速上手并避免常见错误。

weixin_30505225的博客 382

已收藏!kafka服务器最大消息

Part 1微服务架构设计概述 1.1 传统应用架构的问题 1.2 微服务架构是什么 1.3 微服务架构有哪些特点和挑战 1.4 如何搭建微服务架构 Part 2微服务开发框架 2.1 Spring Boot 是什么 2.2 如何使用Spring Boot框架 2.3 Spring Boot生产级特性 Part 3微服务网关 3.1 Node.js 是什么 3.2 如何使用 Node.js 3.3 使用Node.js搭建微服务网关 Part 4微服务注册与发现. 4.1 ZooKeepe

qq_38514574的博客 338

SAP SD核心流程解析:从销售订单到财务结算的实战指南

本文深入解析SAP SD模块从销售订单到财务结算的核心流程,重点剖析了销售订单中的行项目类别如何作为流程的“总开关”,决定了后续交货、开票及财务集成的走向。文章结合实战案例,详细拆解了正常销售、退货、第三方销售、寄售等典型流程的配置要点与国内化适配方案,并阐述了SD与财务模块集成的关键连接点,为顾问和用户提供了一份全面的实战指南。

weixin_29250403的博客 428

2019 第十届蓝桥杯Java省赛B组个人题解

2019 第十届蓝桥杯Java省赛B组个人题解 前言   以下的第十届蓝桥杯Java B组省赛的题目题解只是我个人的题解,提供一些解题思路,仅作参考,如有错误,望大家指出,不甚感激,我会及时更改。   本来想上传到优快云上免费下载,上传之后发现默认是5积分,后来了解到现在的优快云向知识付费靠拢,激励用户上传资源,取消了用户自行定价的功能。   2019 第十届蓝桥杯Java省赛B组题目——CS...

malimingwq的博客 2万+

STM32外围电路硬件解析

复位 :让MCU回到最开始的状态。并且从头开始,重新执行程序 我们什么时候需要复位? 1.烧录的时候 2.程序跑飞了的时候 3.上电复位(上电的时候需要复位) 复位方式: 1.上电复位: --- 一定要有 MCU的Reset是复位引脚,当这个引脚为低电平时,

qq_39577221的博客 1万+

NCBI|叶绿体基因组数据上传

叶绿体基因组NCBI上传 小编最近在NCBI上传了好几次数据…想着可以把具体的上传步骤写下来,供大家参考。以下是叶绿体基因组的上传示例,大家可以瞅瞅 图片 准备工作 1.登录NCBI官网:https://www.ncbi.nlm.nih.gov/, 依次点击“sign in to NCBI”——“See more 3rd party sign in options”——搜索“Microsoft live.com”。 ”sign in to NCBI“ ”See more 3rd party sign i

weifanbio的博客 3903

第十五届蓝桥杯大赛软件赛省赛(Java 大学B组)

第十五届蓝桥杯大赛软件赛省赛 Java 大学 B 组

NOW I GOT ONE WAY 5199

pytorch实现手语识别

连续手语识别项目,中文数据集CSL

masteryi-0018的博客 1万+

2023年第十四届蓝桥杯软件赛省赛总评

第十四届蓝桥杯软件赛总评

罗勇军的博客 1万+

Claude Code auto mode 管理 subagents 的三道安全闸门

文章摘要: Claude Code的auto mode核心在于重构多代理协作的安全边界,将信任拆解为三个检查点:任务委派前评估主会话指令是否越界(v2.1.178新增);执行中强制subagent继承主会话权限模式,禁止自行降级;返回前审查完整动作历史并附加安全警告。这种设计通过动态运行时约束(classifier)与多层防线(tools限制、hooks等)结合,避免安全策略分散化。auto mode并非完全放权,而是通过减少常规提示+后台严格检查实现平衡,与隔离环境专用的bypassPermissions

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 73

【科研建模】Pycaret自动机器学习框架使用流程及多分类项目实战案例详解

对pycaret的使用流程进行梳理,并详细介绍了基于多分类预测的案例

lys_828的博客 781

2023年第十四届蓝桥杯JavaB组省赛真题(题目+全部完整题解)

A、阶乘求和【问题描述】令S= 1!+ 2!+ 3!...,求S的末尾9位数字。提示:答案首位不为0。

冷兮雪的博客 4万+

PHP实现获取本机ip及mac地址​(附完整源码)

PHP实现获取本机ip及mac地址​(附完整源码)

希望我的博客,能帮上你解决学习中工作中所遇到的问题 354

2023第十四届蓝桥杯大赛软件赛省赛C/C++ 大学 B 组(真题&题解)(C++/Java题解)

记录刷题的过程、感悟、题解。 希望能帮到,那些与我一同前行的,来自远方的朋友😉

2302_80067378的博客 1万+

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

蓝桥杯Java大学B组备考

蓝桥杯Java大学B组学习备考一定是多刷题。

2301_77453905的博客 5439

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

第十二届蓝桥杯 2021年省赛真题 (Java 大学B组) 第一场

蓝桥杯 2021年省赛真题 (Java 大学B组 ) #A ASC #B 卡片 #C 直线 #D 货物摆放 #E 路径 #F 时间显示 #G 最少砝码 #H 杨辉三角形 #I 双向排序 #J 括号序列

NOW I GOT ONE WAY 6万+

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

上一篇: 一招制敌的贪心算法
下一篇: 2015年第六届蓝桥杯省赛AB组全部编程题和部分填空题AC代码(仅不包含A组第十题灾后重建)
Androids_lost_Sheep
博客等级 码龄8年 1022粉丝 86原创
评论 5
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值