将给定的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();
}
}