b_51_最大M欄位和(兩個狀態表示+兩種決策)
阿新 • • 發佈:2020-10-28
將給定的N個數劃分為互不相交的M個子段,並且這M個子段的和是最大的
思路
f[i][j]
表示將前i個數劃分成j段後的最大和,下面是決策:
- f[i-1][j]:將a[j]接在第j段後面
- f[k][j-1]:a[j]另起一段,但因為加上只有a[j]這單獨的一段後已經一共有j段了,所以需要從前f[k][j-1]中選j-1段最大的(k∈[i-1,j)),但前i-1段必須每段都要有一個數字把,所以k最小取i-1
注:並不需要用完n個數哦,但還是超時了啊
import java.util.*; import java.math.*; import java.io.*; class Solution { int n,m; long a[],f[][]; void init() throws IOException { Scanner sc = new Scanner(new BufferedInputStream(System.in)); n=sc.nextInt(); m=sc.nextInt(); a=new long[n+5]; f=new long[n+5][m+5]; for (int i=1; i<=n; i++) a[i]=sc.nextLong(); if (m>=n) { long s=0; for (int i=1; i<=n; i++) s+=a[i]; System.out.println(s); } else { long mx=0; for (int j=1; j<=m; j++) for (int i=1; i<=n; i++) { f[i][j]=Math.max(f[i][j], f[i-1][j]+a[i]); for (int k=j-1; k<i; k++) //第i個數自己起一段,從(j-1,i-1)個數中選j-1段 f[i][j]=Math.max(f[i][j], f[k][j-1]); mx=Math.max(mx, f[i][j]); } System.out.println(mx); } } } public class Main{ public static void main(String[] args) throws IOException { Solution s = new Solution(); s.init(); } }
最裡面那層迴圈其實省去的,為什麼?因為每次它的起始位置是j-1,終點是隨著i的增加而增加,所以可作出等價變換
import java.util.*; import java.math.*; import java.io.*; class Solution { int n,m; long a[],f[][]; void init() throws IOException { Scanner sc = new Scanner(new BufferedInputStream(System.in)); n=sc.nextInt(); m=sc.nextInt(); a=new long[n+5]; f=new long[n+5][m+5]; for (int i=1; i<=n; i++) a[i]=sc.nextLong(); if (m>=n) { long s=0; for (int i=1; i<=n; i++) s+=a[i]; System.out.println(s); } else { long mx=0; for (int j=1; j<=m; j++) { long t=0; for (int i=1; i<=n; i++) { f[i][j]=Math.max(f[i][j], f[i-1][j]+a[i]); if (i>=j) { t=Math.max(t, f[i-1][j-1]); //這裡不就是f[k][j-1]嗎,只不過k每次都<i且≥j-1 f[i][j]=Math.max(f[i][j], t+a[i]); } mx=Math.max(mx, f[i][j]); } } System.out.println(mx); } } } public class Main{ public static void main(String[] args) throws IOException { Solution s = new Solution(); s.init(); } }