[题目链接]
http://acm.hdu.edu.cn/showproblem.php?pid=4261
[算法]
首先,有一个结论 :
| a[1] - k | + | a[2] - k | + ... + | a[n] - k | 当k取(a[1],a[2], ... , a[n])的中位数时,式子的值最小
考虑动态维护中位数
我们用一个大根堆和一个小根堆,大根堆中存放前[1..N/2](向上取整)小的数,小根堆中存放[N/2 + 1,N]小的数,还需维护两个变量s1和s2,分别为小根堆中所有数的和和大根堆中所有数的和
这样,我们就可以预处理出每一段的最小值
然后,我们用f[i][j]表示前i个数分成j段取得的最小值,有状态转移方程 :
f[i][j] = min{ f[k][j - 1] + middle( k + 1,i) ) (其中,middle(k + 1,i)表示[k + 1,i]中每个数与中位数的差值和)
答案即为f[n][k]
[代码]
#include<bits/stdc++.h> using namespace std; #define MAXN 2010 #define MAXK 30 const int INF = 2e9; int i,j,k,s1,s2,x,y,n,m,middle; int a[MAXN],sum[MAXN][MAXN]; int f[MAXN][MAXK]; struct Sheap { int tot; int a[MAXN]; inline void clear() { tot = 0; } inline void up(int now) { if (now == 1) return; int fa = now >> 1; if (a[now] < a[fa]) { swap(a[now],a[fa]); up(fa); } } inline void down(int now) { int son = now << 1; if (son > tot) return; if (son + 1 <= tot && a[son + 1] < a[son]) son++; if (a[son] < a[now]) { swap(a[son],a[now]); down(son); } } inline void insert(int x) { a[++tot] = x; up(tot); } inline void del() { swap(a[1],a[tot]); tot--; down(1); } inline int getroot() { return a[1]; } } S; struct Bheap { int tot; int a[MAXN]; inline void clear() { tot = 0; } inline void up(int now) { if (now == 1) return; int fa = now >> 1; if (a[now] > a[fa]) { swap(a[now],a[fa]); up(fa); } } inline void down(int now) { int son = now << 1; if (son > tot) return; if (son + 1 <= tot && a[son + 1] > a[son]) son++; if (a[son] > a[now]) { swap(a[now],a[son]); down(son); } } inline void insert(int x) { a[++tot] = x; up(tot); } inline void del() { swap(a[1],a[tot]); tot--; down(1); } inline int getroot() { return a[1]; } } B; int main() { while (scanf("%d%d",&n,&m) && (n || m)) { for (i = 1; i <= n; i++) scanf("%d",&a[i]); for (i = 1; i <= n; i++) { B.clear(); S.clear(); sum[i][i] = 0; B.insert(a[i]); s1 = a[i]; s2 = 0; for (j = i + 1; j <= n; j++) { if (B.tot <= (j - i) / 2) { B.insert(a[j]); s1 += a[j]; } else { S.insert(a[j]); s2 += a[j]; } x = B.getroot(); y = S.getroot(); if (x > y) { B.del(); s1 -= x; S.del(); s2 -= y; S.insert(x); s2 += x; B.insert(y); s1 += y; } middle = B.getroot(); sum[i][j] = middle * B.tot - s1 + s2 - middle * S.tot; } } for (i = 1; i <= n; i++) { for (j = 1; j <= m; j++) { f[i][j] = INF; } } for (i = 1; i <= n; i++) f[i][1] = sum[1][i]; for (i = 1; i <= n; i++) { for (j = 2; j <= m; j++) { for (k = i - 1; k >= 1; k--) { f[i][j] = min(f[i][j],f[k][j - 1] + sum[k + 1][i]); } } } printf("%d ",f[n][m]); } return 0; }