[POJ1160] [IOI2000] Post Office [区间dp]

[ L i n k \frak{Link} ]


有一些村庄,要在里面选几个建邮局,求每个村庄到最近的邮局的距离和最小可能是多少。

显然可以让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
就不打了(

猜你喜欢

转载自blog.csdn.net/Estia_/article/details/83580578