题目描述
Given an integer number sequence A of length N (1<=N<=1000), we define f(i,j)=(A[i]+A[i+1]+...+A[j])^2 (i<=j). Now you can split the sequence into exactly M (1<=M<= N) succesive parts, and the cost of a part from A[i] to A[j] is f(i,j). The totle cost is the sum of the cost of each part. Please split the sequence with the minimal cost.
输入
At the first of the input comes an integer t indicates the number of cases to follow. Every case starts with a line containing N ans M. The following N lines are A[1], A[2]...A[N], respectively. 0<=A[i]<=100 for every 1<=i<=N.
输出
For each testcase, output one line containing an integer number denoting the
minimal cost of splitting the sequence into exactly M succesive parts.
示例输入
1 5 2 1 3 2 4 5
示例输出
117
提示
来源
山东省第二届ACM大学生程序设计竞赛
这个题到现在没找到O(n*m)的做法,只会O(n*n*m)的做法;
但是普通去做的话会超时,因为0<=A[i],所以可以有一个剪枝,具体请看代码;
还有一个版本的代码,没有剪枝,也是三重循环,能AC,但是还不太理解。
这个题到现在没找到O(n*m)的做法,只会O(n*n*m)的做法;
但是普通去做的话会超时,因为0<=A[i],所以可以有一个剪枝,具体请看代码;
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<climits>
#define LL long long
#define maxn 1100
#define INF LLONG_MAX/2
#define inf 0x3f
using namespace std;
int arr[maxn];
int sum[maxn];
LL dp[maxn][maxn];
int main() {
int T;
scanf("%d", &T);
while (T--) {
int n, m;
scanf("%d%d", &n, &m);
sum[0] = 0;
for (int i = 1; i <= n; ++i) {
scanf("%d", &arr[i]);
sum[i] = sum[i - 1] + arr[i];
}
memset(dp, 0x3f, sizeof(dp));
dp[0][0] = 0;
for(int i=1;i<=n;++i)
{
for(int j=1;j<=i && j<=m;++j)
{
for(int k=i-1;k>=0;--k)
{
LL temp=sum[i]-sum[k];
dp[i][j]=min(dp[i][j],dp[k][j-1]+temp*temp);
if(temp*temp>=dp[i][j])//因为A[i]>0,所以sum[i]-sum[k]<sum[i]-sum[k1],k1<k,所以继续循环也不可能有更优的解
break;
}
}
}
// for (int j = 1; j <= m; ++j) { //也能A,一样,理解的角度有点差别
// for (int i = j; i <= n; ++i) {
// for (int k = 1; k <= i ; ++k) {
// LL temp = sum[i] - sum[i - k];
// dp[i][j] = min(dp[i - k][j - 1] + temp * temp, dp[i][j]);
// if(temp*temp>=dp[i][j])
// break;
// }
// }
// }
printf("%lld\n", dp[n][m]);
}
}
还有一个版本的代码,没有剪枝,也是三重循环,能AC,但是还不太理解。