算法筆記_096:藍橋杯練習 算法提高 求最大值(Java)


目錄

1 問題描述

2 解決方案

 


1 問題描述

問題描述
  給n個有序整數對ai bi,你需要選擇一些整數對 使得所有你選定的數的ai+bi的和最大。並且要求你選定的數對的ai之和非負,bi之和非負。
輸入格式
  輸入的第一行為n,數對的個數
  以下n行每行兩個整數 ai bi
輸出格式
  輸出你選定的數對的ai+bi之和
樣例輸入
5
-403 -625
-847 901
-624 -708
-293 413
886 709
樣例輸出
1715
數據規模和約定
  1<=n<=100
  -1000<=ai,bi<=1000

 


2 解決方案

本題主要考查動態規划思想的運用,下面的具體編碼參考自文末參考資料1,我看了一下文中的講解:

主要核心問題是:題目要求結果中所有ai之和非負,bi的和非負。

那么,首先,對輸入數據處理一下,過濾掉所有ai + bi <= 0的數據對,對於剩下的數據對中,從第一個數據對開始,對於前i行數據,依次求取當ai的和從0200000之間某一個值時,前i行中bi和的最大值。那么當i = n,即最后一行數據時,求取:當dp[i][j] >= 0(PS:bi的和非負)且j >= 100000(PS:ai的和非負,其中的dp[i][j]+j的最大值,即為最終結果。對於200000100000的定義,詳細參考代碼注釋哦。

有點遺憾的是,下面的代碼在系統運行評分為91...代碼僅供參考

 

 

具體代碼如下:

 

import java.util.Scanner;

public class Main {

    public int[][] dp = new int[105][200005];   
    
    public final static int t = 100001;  //ai或者bi所有輸入項和最大為100*1000 = 100000
    public final static int f = -200005;  //ai+bi所有輸入項和最小為100*-1000*2 = -200000
    
    public int getMax(int a, int b) {
        return a > b ? a : b;
    }
    /*
     * dp[i][j]:i表示符合輸入數據中ai + bi > 0的前i項,j表示這i項中ai的和
     * dp[i][j]:其具體含義為當前i項ai和為j時,存放前i項中bi和的最大值
     * 那么,可知共有n項滿足ai + bi > 0,那么找出dp[n][j] + j最大值即為最終結果
     */
    public void printResult(int[] A, int[] B, int len) {
        for(int i = 0;i < len;i++) {
            for(int j = -t;j < t;j++) 
                dp[i][j + t] = f;    //初始化為題目所有輸入數據和最小值,即前i項中bi的和
        }
        for(int i = 0;i < len;i++) {
            //此處,使用t作為防止數組越界標准數,因為A[i]有可能小於0,而數組下標不可能為負數
            dp[i][A[i] + t] = B[i];     //初始化當j = A[i] + t時,此時前i項B的和為B[i]
        }
        for(int i = 1;i < len;i++) {
            for(int j = -t;j < t;j++) {
                dp[i][j + t] = getMax(dp[i - 1][j + t], dp[i][j + t]);
                if(j + t - A[i] < 0 || j + t - A[i] > 200001)  
                    continue;
                //此處判定,作為當j + t中不包含第i項A[i]時,更新當前最大值
                dp[i][j + t] = getMax(dp[i][j + t], dp[i - 1][j + t - A[i]] + B[i]);
            }
        }
        int result = f;
        for(int i = 0;i < t;i++) {
            //可知,最終的dp[i][j]中,其中j >= t,dp[i][j] >= 0,才符合對於sum(ai)和sum(bi)均不小於0的要求
            if(dp[len - 1][i + t] >= 0)
                result = getMax(result, dp[len - 1][i + t] + i);
        }
        if(result <= 0) {
            System.out.println(0);
            return;
        }
        System.out.println(result);
        return;
    }
    
    
    public static void main(String[] args) {
        Main test = new Main();
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int len = 1;
        int[] A = new int[n + 1];
        int[] B = new int[n + 1];
        for(int i = 1;i <= n;i++) {
            int a = in.nextInt();
            int b = in.nextInt();
            if(a + b > 0) { //過濾掉所有a + b <= 0的數據對
                A[len] = a;
                B[len++] = b;
            }
        }
        test.printResult(A, B, len);
    }
}

 

上述錯誤修正如下:

錯誤在輸入數據處理上:即注釋中//過濾掉所有a + b <= 0的數據對,看到文末網友評論,我仔細想了一下數據處理的邏輯,發現如果過濾掉所有a + b<=0的數據對,會過濾掉其中a > 0或者b >  0的情形,導致在求ai的和出現誤差,以及bi的和出現誤差。(PS:即如果ai+a,其中a > 0,那么遇到下一個數據對時,其可以接納的數據對就會增加,同理,對於bi也一樣。)

現在有兩種方案:

(1)把過濾條件修改為如下:if(a < 0 && b < 0)  continue;

(2)不對輸入數據進行過濾處理,直接計算處理所有輸入數據對。

驗證結果如下(PS:在藍橋杯練習系統中評分均為100分):

 

修改后代碼:

import java.util.Scanner;

public class Main {

    public int[][] dp = new int[105][200005];   
    
    public final static int t = 100001;  //ai或者bi所有輸入項和最大為100*1000 = 100000
    public final static int f = -200005;  //ai+bi所有輸入項和最小為100*-1000*2 = -200000
    
    public int getMax(int a, int b) {
        return a > b ? a : b;
    }
    /*
     * dp[i][j]:i表示符合輸入數據中ai + bi > 0的前i項,j表示這i項中ai的和
     * dp[i][j]:其具體含義為當前i項ai和為j時,存放前i項中bi和的最大值
     * 那么,可知共有n項滿足ai + bi > 0,那么找出dp[n][j] + j最大值即為最終結果
     */
    public void printResult(int[] A, int[] B, int len) {
        for(int i = 0;i < len;i++) {
            for(int j = -t;j < t;j++) 
                dp[i][j + t] = f;    //初始化為題目所有輸入數據和最小值,即前i項中bi的和
        }
        for(int i = 0;i < len;i++) {
            //此處,使用t作為防止數組越界標准數,因為A[i]有可能小於0,而數組下標不可能為負數
            dp[i][A[i] + t] = B[i];     //初始化當j = A[i] + t時,此時前i項B的和為B[i]
        }
        for(int i = 1;i < len;i++) {
            for(int j = -t;j < t;j++) {
                dp[i][j + t] = getMax(dp[i - 1][j + t], dp[i][j + t]);
                if(j + t - A[i] < 0 || j + t - A[i] > 200001)  
                    continue;
                //此處判定,作為當j + t中不包含第i項A[i]時,更新當前最大值
                dp[i][j + t] = getMax(dp[i][j + t], dp[i - 1][j + t - A[i]] + B[i]);
            }
        }
        int result = f;
        for(int i = 0;i < t;i++) {
            //可知,最終的dp[i][j]中,其中j >= t,dp[i][j] >= 0,才符合對於sum(ai)和sum(bi)均不小於0的要求
            if(dp[len - 1][i + t] >= 0)
                result = getMax(result, dp[len - 1][i + t] + i);
        }
        if(result <= 0) {
            System.out.println(0);
            return;
        }
        System.out.println(result);
        return;
    }
    
    
    public static void main(String[] args) {
        Main test = new Main();
        Scanner in = new Scanner(System.in);
        //System.out.println("請輸入:");
        int n = in.nextInt();
        int len = 1;
        int[] A = new int[n + 1];
        int[] B = new int[n + 1];
        for(int i = 1;i <= n;i++) {
            int a = in.nextInt();
            int b = in.nextInt();
// if(a < 0 && b < 0) // continue; // if(a + b > 0) { //過濾掉所有a + b <= 0的數據對 A[len] = a; B[len++] = b; // } } test.printResult(A, B, len); } }

 

 

 

 

參考資料:

1.藍橋杯 算法提高 求最大值

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM