poj1160-Post Office(区间动规+四边形不等式优化)

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

int n, m;
int x[305];
int f[35][305], p[400][305];
int w[305][305];
int Left[305][305], Right[305][305];//left[i][j]表示第i+1,i+2,...,j个村庄到第i个村庄的距离和
									// right[i][j]表示第i-1,i-2,...,j个村庄到第i个村庄的距离和
struct poj1160 {
	/*[问题描述]:有n个村庄,其位置分别为x[1],x[2],...,x[n],
	*从这n个村庄中选出m个设立邮局,要求每个村庄到它最近邮局的距离之和最小
	*/

	/*[解题思路]:f[i][j]表示在前j个村庄设i个邮局所得到的最小距离和,则f[i][j]=min(f[i-1][k]+w[k+1][j]),
	*w[k+1][j]表示在第k+1到第j个村庄设一个邮局的最小距离和,显然应该将唯一的邮局设在中间那个村庄.
	*令p[i][j]表示使得f[i][j]最小的那个k,根据四边形不等式优化可知p[i-1][j]<=p[i][j]<=p[i][j+1]
	*/



	void work() {
		cin >> n >> m;
		for (int i = 1; i <= n; i++)
			cin >> x[i];

		for (int i = 1; i <= n; i++) {
			Left[i][i] = 0;
			for (int j = i + 1; j <= n; j++)
				Left[i][j] = Left[i][j - 1] + x[j] - x[i];
		}
		for (int i = n; i >= 1; i--) {
			Right[i][i] = 0;
			for (int j = i - 1; j >= 1; j--)
				Right[i][j] = Right[i][j + 1] + x[i] - x[j];
		}

		memset(w, 0, sizeof(w));
		for (int i = 1; i <= n; i++) {
			for (int j = i + 1; j <= n; j++) {
				int tmp = (j + i) >> 1;
				if ((j - i) % 2 == 0)
					w[i][j] = Right[tmp][i] + Left[tmp][j];
				else
					w[i][j] = min(Right[tmp][i] + Left[tmp][j], Right[tmp + 1][i] + Left[tmp + 1][j]);
			}
		}


		for (int i = 1; i <= n; i++) {
			f[1][i] = w[1][i];
			//p[1][i] = (i + 1) >> 1;
			//p[i][n + 1] = n;
			p[i][i] = i - 1;
		}
		for (int i = 2; i <= m; i++) {
			p[i][n + 1] = n - 1;
			for (int j = n; j >= i; j--) {
				f[i][j] = 1 << 30;
				for (int k = p[i - 1][j]; k <= p[i][j + 1]; k++) {
					if (f[i][j] > f[i - 1][k] + w[k + 1][j]) {
						f[i][j] = f[i - 1][k] + w[k + 1][j];
						p[i][j] = k;
					}
				}
			}
		}
		cout << f[m][n] << endl;
	}
};

int main()
{
	poj1160 solution;
	solution.work();
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值