题目描述见链接 .
设 表示前 个村庄, 建立 个邮局的最优值,
: ,
其中 表示在 放置一个邮局, 管辖 的村庄的最小值, 显然放在 中位数 位置最优,现在考虑如何 计算 ,
设 表示 的距离前缀和, 表示 的距离后缀和, 可以 预处理 .
设 为 的中位数所在村庄编号,
则
然后经过 打表 可知, 满足四边形不等式:
同时与此对应的最优决策 也 满足决策单调性: .
于是可以使用 四边形不等式 优化 ,
先给出 打表/暴力 程序
#include<bits/stdc++.h>
#define reg register
typedef long long ll;
const int maxn = 3005;
int read(){
char c;
int s = 0, flag = 1;
while((c=getchar()) && !isdigit(c))
if(c == '-'){ flag = -1, c = getchar(); break ; }
while(isdigit(c)) s = s*10 + c-'0', c = getchar();
return s * flag;
}
int N;
int K;
int A[maxn];
int p[maxn][maxn];
ll sl[maxn];
ll sr[maxn];
ll sum_l[maxn];
ll sum_r[maxn];
ll F[maxn][maxn];
ll w[maxn][maxn];
int main(){
freopen("a.in", "r", stdin);
freopen("a.out", "w", stdout);
N = read(); K = read();
for(reg int i = 1; i <= N; i ++) A[i] = read();
std::sort(A+1, A+N+1);
for(reg int i = 1; i <= N; i ++) sum_l[i] = sum_l[i-1] + A[i], sl[i] = sum_l[i] + sl[i-1];
for(reg int i = N; i >= 1; i --) sum_r[i] = sum_r[i+1] + A[i], sr[i] = sum_r[i] + sr[i+1];
for(reg int i = 1; i <= N; i ++)
for(reg int j = i; j <= N; j ++){
int m = i+j >> 1;
w[i][j] = (1ll*m-i)*A[m] - (sum_l[m-1] - sum_l[i-1]) + (sum_r[m+1] - sum_r[j+1]) - (1ll*j-m)*A[m];
}
memset(F, 0x3f, sizeof F); F[0][0] = 0;
for(reg int i = 1; i <= N; i ++)
for(reg int j = 1; j <= i; j ++){
for(reg int k = 0; k < i; k ++){
ll t = F[k][j-1] + w[k+1][i];
if(t < F[i][j]){
F[i][j] = t;
p[i][j] = k;
}
}
}
for(reg int i = 1; i <= N; i ++)
for(reg int j = 1; j <= i; j ++)
if(i!=N && j != 1 && (p[i][j-1] > p[i][j] || p[i][j] > p[i+1][j])){
printf("False
");
}else if(i != N && j != i && (F[i+1][j+1] + F[i][j] > F[i+1][j] + F[i][j+1])){
printf("False
");
}
printf("%lld
", F[N][K]);
return 0;
}
#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
#define reg register
typedef long long ll;
const int maxn = 3005;
int read(){
char c;
int s = 0, flag = 1;
while((c=getchar()) && !isdigit(c))
if(c == '-'){ flag = -1, c = getchar(); break ; }
while(isdigit(c)) s = s*10 + c-'0', c = getchar();
return s * flag;
}
int N;
int K;
int A[maxn];
int p[maxn][305];
ll sum_l[maxn];
ll sum_r[maxn];
ll F[maxn][305];
ll w[maxn][maxn];
int main(){
N = read(); K = read();
for(reg int i = 1; i <= N; i ++) A[i] = read();
std::sort(A+1, A+N+1);
for(reg int i = 1; i <= N; i ++) sum_l[i] = sum_l[i-1] + A[i];
for(reg int i = N; i >= 1; i --) sum_r[i] = sum_r[i+1] + A[i];
for(reg int i = 1; i <= N; i ++)
for(reg int j = i; j <= N; j ++){
int m = i+j >> 1;
w[i][j] = (1ll*m-i)*A[m] - (sum_l[m-1] - sum_l[i-1]) + (sum_r[m+1] - sum_r[j+1]) - (1ll*j-m)*A[m];
}
memset(F, 0x3f, sizeof F); F[0][0] = 0;
for(reg int j = 1; j <= K; j ++){
p[N+1][j] = N-1;
for(reg int i = N; i >= 1; i --)
for(reg int k = p[i][j-1]; k <= p[i+1][j]; k ++){
ll t = F[k][j-1] + w[k+1][i];
if(t < F[i][j]) F[i][j] = t, p[i][j] = k;
}
}
printf("%lld
", F[N][K]);
return 0;
}