poj 1160 post office

table[i][j]表示在第i个村庄建立第j个邮局忽略i后面村庄后所能得到的最短距离

table[i][j] = min(table[k][j-1] + ∑dist[k][p]( k<p < mid)+∑dist[q][i](mid <= q < i))  k < i

ans = min(table[i][P]+∑dist[i][k](  k > i)) 

dic[i]为第i个村庄坐标,sum[i]表示前i个村庄的坐标和, k < i时 ,∑dist[p][i] ( k <= p < i)可以用dic[i]*(i-k+1)-(sum[i]-sum[k-1])的到,k > i时,sum[k]-sum[i-1]-dic[i]*(k-i+1)

中点用二分查找得到

复杂度为O(V^2*(logV+P))


#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <queue>
#include <algorithm>
#include <vector>
#include <cstring>
#include <stack>
#include <cctype>
#include <utility>   
#include <map>
#include <string>  
#include <climits> 
#include <set>
#include <string> 
#include <sstream>
#include <utility>
#include <ctime>

using std::priority_queue;
using std::vector;
using std::swap;
using std::stack;
using std::sort;
using std::max;
using std::min;
using std::pair;
using std::map;
using std::string;
using std::cin;
using std::cout;
using std::set;
using std::queue;
using std::string;
using std::istringstream;
using std::make_pair;
using std::greater;

const int MAXN(310);
const int INFI(INT_MAX-1);

int dic[MAXN];
int sum1[MAXN];


int find(int goal, int l, int r)
{
	while(l < r)
	{
		int m = (l+r) >> 1;
		if(dic[m] <= goal)
			l = m+1;
		else
			r = m;
	}
	return l;
}

int table[MAXN][35];

int main()
{	
	int V, P;
	while(~scanf("%d%d", &V, &P))
	{
		for(int i = 1; i <= V; ++i)
		{
			scanf("%d", dic+i);
			sum1[i] = dic[i]+sum1[i-1];
		}
		for(int i = 2; i <= V; ++i)
		{
			table[i][1] = dic[i]*i-sum1[i];
			for(int j = 2; j <= i; ++j)
				table[i][j] = INFI;
			int up = min(i, P);
			for(int j = i-1; j >= 1; --j)
			{
				int tu = min(j+1, up);
				int ind = find((dic[i]+dic[j])/2, j, i);
				int temp =	dic[i]*(i-ind+1)-(sum1[i]-sum1[ind-1])+(sum1[ind-1]-sum1[j-1])-dic[j]*(ind-1-j+1);
				for(int k = 2; k <= tu; ++k)
					table[i][k] = min(table[i][k], table[j][k-1]+temp);
			}
		}
		int ans = INFI;
		for(int i = P; i <= V; ++i)
			ans = min(ans, table[i][P]+sum1[V]-sum1[i-1]-dic[i]*(V-i+1));
		printf("%d\n", ans);
	}
	return 0;
}

另一个思路

设table[i][j]表示前j个村庄建立i个邮局的最优解,w[i][j]表示在i到j的村庄建立邮局的最下距离(显然在中位数出建立是最小的)

table[i][j] = min(table[i-1][k])   i-1 <= k < j

边界:  table[1][k] = w[1][k]

可以使用四边形不等式优化,但是递推顺序要改一下

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <queue>
#include <algorithm>
#include <vector>
#include <cstring>
#include <stack>
#include <cctype>
#include <utility>   
#include <map>
#include <string>  
#include <climits> 
#include <set>
#include <string> 
#include <sstream>
#include <utility>
#include <ctime>

using std::priority_queue;
using std::vector;
using std::swap;
using std::stack;
using std::sort;
using std::max;
using std::min;
using std::pair;
using std::map;
using std::string;
using std::cin;
using std::cout;
using std::set;
using std::queue;
using std::string;
using std::istringstream;
using std::make_pair;
using std::greater;

const int MAXV(310);
const int MAXP(40);
const int INFI(INT_MAX-1);

int table[MAXP][MAXV];
int dic[MAXV], sum[MAXV];

int get_value(int a, int b)
{
	if(a <= b)
	{
		return sum[b]-sum[a-1]-dic[a]*(b-a+1);
	}
	else
	{
		return dic[a]*(a-b+1)-(sum[a]-sum[b-1]);
	}
}

int main()
{
	int V, P;
	while(~scanf("%d%d", &V, &P))
	{
		for(int i = 1; i <= V; ++i)
		{
			scanf("%d", dic+i);
			sum[i] = sum[i-1]+dic[i];
		}
		for(int j = 1; j <= V; ++j)
			table[1][j] = get_value((j+1)/2, 1)+get_value((j+1)/2, j);
		for(int i = 2; i <= P; ++i)
			for(int j = i; j <= V; ++j)
			{
				table[i][j] = INFI;
				int temp = i-1;
				for(int k = j-1; k >= temp; --k)
				{
					int mid = (k+1+j)/2;
					table[i][j] = min(table[i][j], table[temp][k]+get_value(mid, k+1)+get_value(mid, j));
				}
			}
		printf("%d\n", table[P][V]);
	}
	return 0;
}


四边形不等式优化(写的比较挫 = = )


#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <queue>
#include <algorithm>
#include <vector>
#include <cstring>
#include <stack>
#include <cctype>
#include <utility>   
#include <map>
#include <string>  
#include <climits> 
#include <set>
#include <string> 
#include <sstream>
#include <utility>
#include <ctime>


using std::priority_queue;
using std::vector;
using std::swap;
using std::stack;
using std::sort;
using std::max;
using std::min;
using std::pair;
using std::map;
using std::string;
using std::cin;
using std::cout;
using std::set;
using std::queue;
using std::string;
using std::istringstream;
using std::make_pair;
using std::greater;


const int MAXV(310);
const int MAXP(40);
const int INFI(INT_MAX-1);


int table[MAXP][MAXV];
int dic[MAXV], sum[MAXV];
int opt[MAXP][MAXV];


int get_value(int a, int b)
{
	if(a <= b)
	{
		return sum[b]-sum[a-1]-dic[a]*(b-a+1);
	}
	else
	{
		return dic[a]*(a-b+1)-(sum[a]-sum[b-1]);
	}
}


int main()
{
	int V, P;
	while(~scanf("%d%d", &V, &P))
	{
		for(int i = 1; i <= V; ++i)
		{
			scanf("%d", dic+i);
			sum[i] = sum[i-1]+dic[i];
		}
		for(int j = 1; j <= V; ++j)
		{
			int mid = (1+j)/2;
			table[1][j] = get_value(mid, 1)+get_value(mid, j);
			opt[1][j] = 1;
		}
		for(int i = 2; i <= P; ++i)
		{
			table[i][V] = INFI;
			for(int j = opt[i-1][V]; j < V; ++j)
			{
				int mid = (j+1+V)/2; 
				int tv = table[i-1][j]+get_value(mid, j+1)+get_value(mid, V);


				if(tv < table[i][V])
				{
					table[i][V] = tv;
					opt[i][V] = j;
				}
			}
			for(int j = V-1; j >= i; --j)
			{
				table[i][j] = INFI;
				int temp = opt[i][j+1];
				for(int k = opt[i-1][j]; k <= temp; ++k)
				{
					int mid = (k+1+j)/2;
					int tv = table[i-1][k]+get_value(mid, k+1)+get_value(mid, j);
					if(tv < table[i][j])
					{
						table[i][j] = tv;
						opt[i][j] = k;
					}
				}
			}
		}
		printf("%d\n", table[P][V]);
	}
	return 0;
}



你可能感兴趣的:(poj 1160 post office)