首先解决一个问题:
给一行数,可以从两端拿取,问拿m次后所取的数的和最大是多少?
思路:左边拿多少确定以后,只要枚举一下右边的个数就可以了
代码:
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] argv) throws IOException
{
Scanner in = new Scanner(new BufferedInputStream(System.in));
int[] shelf = new int[1000];
int[] D = new int[1000];
int n = in.nextInt();
int num = in.nextInt();
for(int i =1 ; i <= n ; i++)
{
shelf[i] = in.nextInt();
shelf[i] += shelf[i-1]; //求和预处理
}
for(int i = 0 ; i <= n ; i++) //左边选n-i个之后,右边枚举选j个
for(int j = 0 ; j <= i ; j++)
D[n-i+j] = Math.max(D[n-i+j] , shelf[n] - shelf[i] + shelf[j]);
System.out.println(D[num]);
}
}
那么本题的思路:
先构造一个小动规D,D[i]记录取i个数时的最大和(首先预处理)
然后构造一个整体dp,dp[i]表示仅考虑前几行时,取i个数得到的最大和
状态转移方程:
dp[i][j] = max(dp[i-1][j-k] + D[k]);
我们要枚举的就是k了
总体代码:
import java.io.*;
import java.util.*;
public class Main {
static int[] shelf = new int[10100];
static int n,m;
static int[] dp = new int[10100];
static int[] D = new int[10100];
public static void main(String[] argv)
{
Scanner in = new Scanner(new BufferedInputStream(System.in));
n=in.nextInt();
m=in.nextInt();
for(int i = 1 ; i <= n ; i++)
{
int a = in.nextInt();
for(int j=1 ; j <= a ; j++)
{
shelf[j] = in.nextInt();
shelf[j] += shelf[j-1]; //sum to preprocess
}
Arrays.fill(D,0);
for(int j = 0 ; j < a ; j++) //make D,D[x] represent that we break x will make the maximum damage
for(int k = j ; k <= a ; k++) //take j from left and,take a-k from the right
D[a+j-k] = Math.max(D[a+j-k] , shelf[a] + shelf[j] - shelf[k]);
for(int j = m ; j>=1 ; j--)
for(int k = Math.min(j,a) ; k>=1 ; k--)
dp[j] = Math.max(dp[j],dp[j-k] + D[k]); //dp[j] = the front i-1 line berak j-k times and current line break k times will make the maximum damage
}
System.out.println(dp[m]);
}
}