二维dp数组java
dp[i][j]前j个数字选取i段的最大和,必定要第j个数字数组
dp[i][j]=dp[i][j-1]+a[j-1] 这是指把第j个数字并入前面那些数字的最后一段优化
dp[i][j]=Max(dp[i][j],dp[i-1][m]+a[j-1]) 这是把第j个数字单独做为一段spa
时间复杂度(O(k*n^2))code
public int max_M_sum(int[] a,int k) { int[][] dp=new int[k+1][a.length+1];//dp[i][j]前j个数字选取i段的最大和,必定包含第j个数字 dp[1][1]=a[0]; for(int i=2;i<k+1;i++) dp[i][i]=dp[i-1][i-1]+a[i-1]; for(int i=1;i<k+1;i++) { for(int j=i+1;j<a.length+1;j++) { dp[i][j]=dp[i][j-1]+a[j-1];//至关于把第j个数链接到前面的最后一段后面 for(int temp=i-1;temp<j;temp++) { dp[i][j]=Math.max(dp[i][j],dp[i-1][temp]+a[j-1]); } } } return dp[k][a.length];//这里返回的不是最优解 }
观察最上面的图发现,dp[i][j]只由左边的一位和上面的一段决定,并且是由上面一段的最大值决定的,所以能够进行优化blog
时间复杂度O(k*n)get
public int max_M_sum(int[] a,int k) { int[] pre=new int[a.length+1];//pre[i]少一段的最大值,从1到i之间的最大值 int[] dp=new int[a.length+1]; int tmp=-999999; for(int i=1;i<k+1;i++) { tmp=-999999;//不断刷新最左边的那个空的 for(int j=i;j<a.length+1;j++) { dp[j] = Math.max(dp[j-1], pre[j-1])+a[j-1]; pre[j-1]=tmp; tmp=Math.max(tmp, dp[j]); } } return tmp; }
这个笔试题,最终的段数能够小于k,求最大的就行,所以须要合并一下,减少复杂度,但仍是过不去,我也不知道为何class
import java.util.*; public class Main{ public static void main(String[] args) { Scanner sc=new Scanner(System.in); int n=sc.nextInt(); int k=sc.nextInt(); int[] a=new int[n]; for(int i=0;i<n;i++) { a[i]=sc.nextInt(); } int t=0; int[] b=new int[n]; b[0]=a[0]; for(int i=1;i<n;i++) { if(a[i]*a[i-1]>0) {//同号 b[t]+=a[i]; } else { t++; b[t]=a[i]; } } Main m=new Main(); System.out.println(m.max_M_sum(b,k,t+1)); } public int max_M_sum(int[] a,int k,int t) { if(t<=k) { int res=0; for(int i=0;i<t;i++) { if(a[i]>0) res+=a[i]; } return res; } else{ int[] pre=new int[t+1];//少一段的最大值,不必定包含最后一个 int[] dp=new int[t+1]; int tmp=-999999; int res=Integer.MIN_VALUE; for(int i=1;i<k+1;i++) { tmp=Integer.MIN_VALUE;//不断刷新最左边的那个空的 for(int j=i;j<t+1;j++) { dp[j] = Math.max(dp[j-1], pre[j-1])+a[j-1]; pre[j-1]=tmp; tmp=Math.max(tmp, dp[j]); } } for(int i=0;i<t+1;i++) res=Math.max(res, dp[i]); return res; } } }