SDUTOJ 2169 Sequence 山东省第二届省赛

题目描述

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],所以可以有一个剪枝,具体请看代码;

#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,但是还不太理解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值