有一些村庄,要在里面选几个建邮局,求每个村庄到最近的邮局的距离和最小可能是多少。
显然可以让f(i,j)表示前i个邮局,前j个村庄。
令w(i,j)表示一个邮局覆盖[i,j]的最小代价,很容易列出方程
f(i,j)=min{f(i-1,k)+w(k+1,j)}
典型的顺序递推区间问题转移方程。
同时也是典型的可能可以用四边形不等式/斜率优化的方程。
总之先打个Θ(n3)刷决策单调表。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<ctime>
using std::cin;
using std::cout;
using std::endl;
int nNumVillages;
int nNumPostOffices;
int nPosVillages[305];
int nLeastSum[305][305];
int nDisSum[305][305];
int nPoint[305][305];
inline void init() {
// std::ios::sync_with_stdio(false);
// cin.tie(0);
// cout.tie(0);
}
void input() {
cin >> nNumVillages >> nNumPostOffices;
for (register int i = 1; i <= nNumVillages; ++i) {
cin >> nPosVillages[i];
}
}
void work() {
for (register int i = 1; i <= nNumVillages; ++i) {
for (register int j = i + 1; j <= nNumVillages; ++j) {
nDisSum[i][j] = nDisSum[i][j-1] + nPosVillages[j] - nPosVillages[i+j>>1];
}
}
memset(nLeastSum, 0x3f, sizeof(nLeastSum));
nLeastSum[0][0] = 0;
for (register int i = 1; i <= nNumPostOffices; ++i) {
for (register int j = 1; j <= nNumVillages; ++j) {
for (register int k = 0; k < j; ++k) {
if (nLeastSum[i-1][k] + nDisSum[k+1][j] < nLeastSum[i][j]) {
nLeastSum[i][j] = nLeastSum[i-1][k] + nDisSum[k+1][j];
nPoint[i][j] = k;
}
}
printf("%3d",nPoint[i][j]);
}
putchar('\n');
}
}
inline void output() {
cout << nLeastSum[nNumPostOffices][nNumVillages];
}
int main() {
init();
input();
work();
output();
return 0;
}
这道题就A了,还跑得挺快的
对样例打出来表长这样:
0
0
0
0
0
0
0
0
0
0
0
1
1
3
3
3
3
7
8
8
0
0
2
3
3
5
5
7
8
8
0
0
0
3
3
5
6
7
8
8
0
0
0
0
4
5
6
7
8
9
再多打几个表就能发现决策单调。
优化有点麻烦,上面的循环是不能直接逆序的,得改一维为枚举区间长
优化后把w用前缀和差分求,复杂度约Θ((V-P)V)。
code就不打了(