[POJ1160] [IOI2000] Post Office [區間dp]
阿新 • • 發佈:2018-11-02
有一些村莊,要在裡面選幾個建郵局,求每個村莊到最近的郵局的距離和最小可能是多少。
顯然可以讓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就不打了(