HDU_3480
首先,一个贪心的思路就是如果将元素排个序,选择的一定是连续的区间,因为如果选了两个值,那么这个区间就可以选两个值之间的任何一个值,这样是不会改变这个区间的cost的。
如果用w[i][j]表示(a[j]-a[i])*(a[j]-a[i]),写出状态转移方程后会得到f[i][j]=min{f[i-1][k-1]+w[k,j]},这个和POJ_1160的状态转移方程是一样的,如果我们能够证明w为凸,那么这个题就可以用四边形不等式优化。
由于w[i+1][j]-w[i][j]=(a[i]-a[i+1])*(2*a[j]-a[i+1]-a[i]),这个表达式是随着j的增加单调递减的,所以就有w[i+1][j+1]-w[i][j+1]<=w[i+1][j]-w[i][j],也就是w[i][j]+w[i+1][j+1]<=w[i][j+1]+w[i+1][j],所以w是凸的,进而我们就可以放心地使用四边形不等式优化dp了。
其实,如果我们把状态转移方程写成f[i][j]=min{f[i-1][k-1]+ (a[j]-a[k])*(a[j]-a[k])}的话,不妨假设k的取值x、y满足x<y,且y要比x优,这时就能化简成a[j]>(a[y]*a[y]-a[x]*a[x]+f[i-1][y-1]-f[i-1][x-1])/(2*a[y]-2*a[x]),这样就又变成能够使用斜率优化了,而且用斜率优化比较好用滚动数组实现,能节省不少空间。
//四边形不等式优化dp
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define MAXD 10010
#define INF 0x3f3f3f3f
int N, M, a[MAXD], f[MAXD][MAXD], K[MAXD][MAXD];
int cmp(const void *_p, const void *_q)
{
int *p = (int *)_p, *q = (int *)_q;
return *p - *q;
}
int getw(int x, int y)
{
return (a[y] - a[x]) * (a[y] - a[x]);
}
void init()
{
int i, j, k;
scanf("%d%d", &N, &M);
for(i = 1; i <= N; i ++)
scanf("%d", &a[i]);
}
void solve()
{
int i, j, k, p, t;
qsort(a + 1, N, sizeof(a[0]), cmp);
for(i = 0; i <= N; i ++)
{
f[i][i] = 0;
K[i][i] = i;
}
for(i = M + 2; i <= N; i ++)
K[M + 1][i] = i;
for(p = 1; p <= N - M; p ++)
{
f[0][p] = INF;
for(i = 1; i <= M; i ++)
{
j = i + p;
f[i][j] = INF;
for(k = K[i][j - 1]; k <= K[i + 1][j]; k ++)
{
t = f[i - 1][k - 1] + getw(k, j);
if(t < f[i][j])
{
f[i][j] = t;
K[i][j] = k;
}
}
}
}
printf("%d\n", f[M][N]);
}
int main()
{
int t, tt;
scanf("%d", &t);
for(tt = 0; tt < t; tt ++)
{
init();
printf("Case %d: ", tt + 1);
if(M >= N)
printf("0\n");
else
solve();
}
return 0;
}
//斜率优化+单调队列
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define MAXD 10010
int N, M, a[MAXD], wa[MAXD], wb[MAXD], *f, *g, q[MAXD];
int cmp(const void *_p, const void *_q)
{
int *p = (int *)_p, *q = (int *)_q;
return *p - *q;
}
void init()
{
int i, j, k;
scanf("%d%d", &N, &M);
for(i = 1; i <= N; i ++)
scanf("%d", &a[i]);
}
int getw(int x, int y)
{
return (a[y] - a[x]) * (a[y] - a[x]);
}
int getf(int i)
{
return a[i] * a[i] + g[i - 1];
}
void solve()
{
int i, j, k, front, rear, x, y, z, *t;
qsort(a + 1, N, sizeof(a[0]), cmp);
f = wa, g = wb;
for(i = 1; i <= N; i ++)
g[i] = getw(1, i);
for(i = 2; i <= M; i ++)
{
front = rear = 0;
q[rear ++] = i;
for(j = i; j <= N; j ++)
{
while(front < rear - 1)
{
x = q[front], y = q[front + 1];
if(a[j] * 2 * (a[y] - a[x]) < getf(y) - getf(x))
break;
++ front;
}
x = q[front];
f[j] = g[x - 1] + getw(x, j);
if(j < N)
{
q[rear] = j + 1;
for(k = rear - 1; k > front; k --)
{
x = q[k - 1], y = q[k], z = q[k + 1];
if((getf(y) - getf(x)) * (a[z] - a[y]) < (getf(z) - getf(y)) * (a[y] - a[x]))
break;
q[rear = k] = q[k + 1];
}
++ rear;
}
}
t = f, f = g, g = t;
}
printf("%d\n", g[N]);
}
int main()
{
int t, tt;
scanf("%d", &t);
for(tt = 0; tt < t; tt ++)
{
init();
printf("Case %d: ", tt + 1);
if(M >= N)
printf("0\n");
else
solve();
}
return 0;
}