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; }