hdu 2829 Lawrence

斜率dp

#include <STDIO.H>
#include <STRING.H>
// 保证不超时,斜率dp
/*
	dp[i][x] 前 i 个点,去掉j条边所获破坏的最大值
	res = tol - dp[i][x]
	dp[i][x] = max( dp[j][x-1] + sum[j]*(sum[i]-sum[j])) x-1 < j < i
	斜率dp:
	sum[i]递增
	if k<j, dp[j][x-1] >= dp[k][x-1]
	dp[k][x-1] + sum[k]*(sum[i]-sum[k]) <= dp[j][x-1] + sum[j]*(sum[i]-sum[j])
	then
		(sum[j]*sum[j] - dp[j][x-1]) - (sum[k]*sum[k] - dp[k][x-1]) <= sum[i]*(sum[j] - sum[k])

	if aj = (sum[j]*sum[j] - dp[j][x-1])
	   bj = sum[j]	
	   sum[i] 值不变
	then
		d(k,j) = (aj-ak)/(bj-bk)

	结论:
		1. 若d(k,j)>d(j,i) 则排除j
		2. 若d(j,i)<sum[i] 排除 j
 */
const int MAXN = 1005;
int n, m;
int sum[MAXN];
int a[MAXN];
int dp[MAXN][MAXN];
int tol;
int q[MAXN];

int DP(int i, int x, int j)
{
	return dp[j][x-1] + sum[j]*(sum[i]-sum[j]);
}
int uDP(int k, int j, int x)
{
	return sum[j]*sum[j] - dp[j][x-1] - sum[k]*sum[k] + dp[k][x-1];
}
int dDP(int k, int j)
{
	return sum[j] - sum[k];
}
int main()   
{
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
#endif
	while (scanf("%d%d", &n, &m) == 2 && (n+m))
	{
		int i;
		sum[0] = 0;
		for (i = 1; i<= n; ++i)
		{
			scanf("%d", &a[i]);
			sum[i] = sum[i-1]+a[i];
		}
		tol = 0;
		for (i = n; i>0; --i)
			tol += a[i]*sum[i-1];
		memset(dp, 0, sizeof dp);
		int head, tail;
		for (int x = 1; x <= m; x++)
		{
			head = tail = 0;
			q[tail++] = x;
			for ( i = x+1; i<= n; ++i)
			{
				// 2.
				while (tail-head>1 && uDP(q[head],q[head+1],x) <= sum[i]*dDP(q[head],q[head+1]))
					++head;
				dp[i][x] = DP(i,x,q[head]);
				// 1.
				while (tail-head>1 && uDP(q[tail-2],q[tail-1],x)*dDP(q[tail-1],i)
					>=uDP(q[tail-1],i,x)*dDP(q[tail-2],q[tail-1]))
					tail--;
				q[tail++] = i;
			}
		}
		printf("%d\n", tol-dp[n][m]);
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值