IOI2000啊,比NOIIP还简单。。
用四边形不等式优化了半天都没写对,最后优化了点点系数。
很简单的题。
f[i][j]表示前i个村庄,总共建j个邮局的最小距离和。
预处理可以维护[p,q]区间建一个邮局的最小费用。
用中位数可以用O(n)维护出来(我用前缀和写的o(n^2)的,但是关系不大)
要注意就是如果数量为偶数,中位数会取中间的某一个。
f[i][j] = min(f[k][j-1]+w[k+1][i])
(f[i][0] = maxlong)
k=s[i-1][j-1]就是那个没写完整的四边形不等式优化
后来听说是要改变拓扑序,按区间动规来。前i个邮局是左边界,前j个村庄是右边界。然后就可以用四边形不等式了。
至今未调出来。。
//#include <iostream>
//using std::cout;
//using std::cin;
#define debug(a) printf("%ld",a);
#define debugc(c) printf("%c",c);
#include <cstdio>
const long oo = 0x7fff0000;
long n;long m;
long val[500];
long sum[500];
long w[500][500];
long f[500][50];
long g[500][50];
long s[500][500];
void output(long p,long q)
{
if (p==0)
{
return;
}
output(g[p][q],q-1);
printf("%ld ",p);
}
int main()
{
freopen("post.in","r",stdin);
freopen("post.out","w",stdout);
scanf("%ld%ld",&n,&m);
for (long i=1;i<n+1;i++)
{
scanf("%ld",val+i);
sum[i] = sum[i-1]+val[i];
}
for (long i=1;i<n+1;i++)
{
for (long j=i;j<n+1;j++)
{
long t = (i+j)>>1;
w[i][j] = (sum[j]-sum[t])-(sum[t-1]-sum[i-1])+((t-i)-(j-t))*val[t];
//s[i][j] = t;
if (((j-i+1)&1)==0)
{
t = (i+j+1)>>1;
if (w[i][j]>(sum[j]-sum[t])-(sum[t-1]-sum[i-1])+((t-i)-(j-t))*val[t])
{
w[i][j] = (sum[j]-sum[t])-(sum[t-1]-sum[i-1])+((t-i)-(j-t))*val[t];
//s[i][j] = t;
}
}
}
}
f[0][0] = 0;
f[1][1] = 0;
for (long i=1;i<n+1;i++)
{
f[i][0] = oo;
}
for (long i=1;i<n+1;i++)
{
for (long j=1;j<m+1;j++)
{
f[i][j] = oo;
for (long k=s[i-1][j-1];k<=n&&k<i;k++)
{
if (f[k][j-1]<oo&&f[i][j] > f[k][j-1]+w[k+1][i])
{
s[i][j] = k;
f[i][j] = f[k][j-1]+w[k+1][i];
//g[i][j] = k;
}
}
}
}
printf("%ld",f[n][m]);
//output(n,m);
return 0;
}