第十屆藍橋杯軟件類省賽 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(5n)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條信息模擬每家店的優先級加減情況。最後判斷一下哪些店位於優先緩存。

數據規模最大是10510^5,所以該題的時間複雜度必須小於o(n2)o(n^2)

如果我們枚舉從1到T的每個時刻,然後判斷每個時刻有哪些店接到了訂單,哪些店沒有接到訂單,因爲T和N都是10510^5,所以這樣的時間複雜度是O(n2)O( n^2)會超時,所以我們換種方式枚舉。

因爲只有M條信息中出現的店才接到了訂單,所以我們只需要考慮有訂單的店,因爲其他的店的優先級一定都是0.

然後我們可以用這M條信息模擬接到訂單的店的優先級的加減情況。
第一步:排序
我們可以先對M條信息按時刻從小到大進行排序,如果時刻相同就按店號從小到大排序。時間複雜度爲O(nlogn)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(nlogn)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

我這裏大致說一下思路:

由題目中給的操作我們可以發現,對一個 aia_i 進行一次靈能傳輸,對其前綴和的影響是交換了SiSi1S_i 和 S_{i-1}的位置

即一次靈能傳輸對前綴和的影響是:

Si1SiSi+1(S_{i-1},S_i ,S{i+1}) — > SiSi1Si+1(S_{i},S_{i-1} ,S{i+1})

我們要求的就是通過交換[S1...Sn1][S_1 ...S_n-1]使得S10S2S1...SnSn1|S_1 - 0| |S_2 - S_1|...|S_n - S_{n-1}|的中的最大值達到最小。注意 SnS_n 的位置不能變,因爲無論怎麼操作,SnS_n 的值始終不變。又因爲 S1S_1 的值一定會減去0,所以我們直接將S00S_0 設爲 0 ,並且S0SnS_0 和 S_n的位置是不能變的。

如果排完序後,0是最小值,SnS_n是最大值,那麼我們直接遍歷整個前綴和數組,得到的最大差值的絕對值,就是答案。因爲在此種前綴和的排列方式中,無法找到另一種排列方式使得兩個前綴和之間的差值變的更小。

比如 序列 0 1 2 3 4 5 6

改變其中任意兩個數的位置,都會使差值變大。

但是如果有負數,或者{S_n}不是最大值,直接排序就會出錯。

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

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

答案爲 4

爲什麼這樣排列呢, 因爲 0 要先 到達最小值 然後由最小值到達最大值,再由最大值到達SnS_n是最優的, 如果 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();
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章