【HDU】2829 Lawrence

http://acm.hdu.edu.cn/showproblem.php?pid=2829

题意:将长度为n的序列分成p+1块,使得$sum_{每块}sum_{i<j} a[i]a[j]$最小(n<=1000,1<=a[i]<=100)

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <iostream>
using namespace std;
const int N=10005;
typedef long long ll;
ll d[2][N], s1[N], s2[N];
int n, p, x[N], s[2][N];
inline ll sqr(ll a) { return a*a; }
inline ll w(int l, int r) { return (sqr(s1[r]-s1[l-1])-(s2[r]-s2[l-1]))>>1; }
int main() {
	while(scanf("%d%d", &n, &p), !(n==0&&p==0)) {
		++p;
		for(int i=1; i<=n; ++i) scanf("%d", &x[i]), s1[i]=s1[i-1]+x[i], s2[i]=s2[i-1]+sqr(x[i]);
		int h=0, t=1;
		for(int i=2; i<=n; ++i) d[h][i]=w(1, i), s[h][i]=1;
		for(int i=2; i<=p; ++i) {
			s[t][n+1]=n;
			for(int j=n; j>=1; --j) {
				int l=s[h][j], r=s[t][j+1], &pos=s[t][j]; ll &now=d[t][j];
				now=~0ull>>1;
				for(int k=l; k<=r; ++k) {
					ll t=d[h][k-1]+w(k, j);
					if(now>=t) now=t, pos=k;
				}
			}
			swap(t, h);
		}
		printf("%lld
", d[h][n]);
	}
	return 0;
}

  

设$d(i, j)$表示分$i$份前$j$个的最小代价,容易得到

$$d(i, j) = min { d(i-1, k-1) + w(k, j) }, i<j $$

其中

$$w(i, j) = frac{sum1(i, j)^2-sum2(i, j)}{-2}$$

其中$sum1(i, j)=sum_{i}^{j} a[i], sum2(i, j)=sum_{i}^{j} a[i]^2$

(可以由$(sum_{i} a[i] )^2 = sum_{i} a[i]^2 - 2 sum_{i<j} a[i]a[j]$得到= =,然后$O(n)$求出来,虽然你们都是用$O(n^2)$预处理的= =(我有常数强迫症= =))

证明挺好证的(这里只证明$w$的四边形不等式,剩下证明与前面两题相同= =我就不证明了= =

证明四边形不等式即证明当$j$固定时,$w(i, j+1)-w(i, j)$是关于$i$的递减函数

$$
egin{align}
& w(i, j+1)-w(i, j) otag \
= & frac{sum1(i, j+1)^2-sum2(i, j+1)}{-2} - frac{sum1(i, j)^2-sum2(i, j)}{-2} \
= & frac{sum1(i, j+1)^2 - sum1(i, j)^2 + sum2(i, j) - sum2(i, j+1)}{-2} \
= & frac{a[j+1]^2 - 2a[j+1]sum_{k=i}^{j} a[k] - a[j+1]^2}{-2} \
= & a[j+1]sum_{k=i}^{j} a[k]
end{align}
$$

当j不变时i递增,显然式子减小,得证。

原文地址:https://www.cnblogs.com/iwtwiioi/p/4324406.html