目錄
1 問題描述
問題描述
給n個有序整數對ai bi,你需要選擇一些整數對 使得所有你選定的數的ai+bi的和最大。並且要求你選定的數對的ai之和非負,bi之和非負。
輸入格式
輸入的第一行為n,數對的個數
以下n行每行兩個整數 ai bi
以下n行每行兩個整數 ai bi
輸出格式
輸出你選定的數對的ai+bi之和
樣例輸入
5
-403 -625
-847 901
-624 -708
-293 413
886 709
-403 -625
-847 901
-624 -708
-293 413
886 709
樣例輸出
1715
數據規模和約定
1<=n<=100
-1000<=ai,bi<=1000
-1000<=ai,bi<=1000
2 解決方案
本題主要考查動態規划思想的運用,下面的具體編碼參考自文末參考資料1,我看了一下文中的講解:
主要核心問題是:題目要求結果中所有ai之和非負,bi的和非負。
那么,首先,對輸入數據處理一下,過濾掉所有ai + bi <= 0的數據對,對於剩下的數據對中,從第一個數據對開始,對於前i行數據,依次求取當ai的和從0到200000之間某一個值時,前i行中bi和的最大值。那么當i = n,即最后一行數據時,求取:當dp[i][j] >= 0(PS:bi的和非負)且j >= 100000(PS:ai的和非負),其中的dp[i][j]+j的最大值,即為最終結果。對於200000和100000的定義,詳細參考代碼注釋哦。
有點遺憾的是,下面的代碼在系統運行評分為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); } }
參考資料: