题意:有n个村庄,要在其中m个村庄里建邮局,每个村庄去邮局的代价为当前村庄到最近的一个有邮局村庄的路程,问总最小代价是多少。
解法:dp。dp[i][j]表示在前j个村庄建立i个邮局后的代价,则状态转移方程:dp[i][j] = min{dp[i - 1][k] + f(k + 1, j)},k = i - 1 ... j - 1,f(k + 1, j)表示在k + 1到j这些村庄中放一个邮局的代价,根据贪心的思想,这个邮局应该放在中间的村庄,即第(k + 1 + j) / 2个村庄,暴力的去算f会T,于是用数组sum[i][j]表示只有一个邮局在第i个村庄时,前j个村庄产生的代价,则邮局在第i个村庄时,从j到k村庄的代价为sum[i][k] - sum[i][j - 1]。
代码:
#include<stdio.h> #include<iostream> #include<algorithm> #include<string> #include<string.h> #include<math.h> #include<limits.h> #include<time.h> #include<stdlib.h> #include<map> #include<queue> #include<set> #include<stack> #include<vector> #define LL long long using namespace std; int v[305]; int dp[35][305]; int sum[305][305]; int n, m; void init() { for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) sum[i][j] = sum[i][j - 1] + abs(v[i] - v[j]); } int main() { while(~scanf("%d%d", &n, &m)) { memset(dp, 0, sizeof dp); memset(sum, 0, sizeof sum); for(int i = 1; i <= n; i++) { scanf("%d", &v[i]); } sort(v, v + n); init(); for(int i = 2; i <= n; i++) { int mid = (i + 1) >> 1; dp[1][i] = min(sum[mid][i], sum[mid + 1][i]); } for(int i = 2; i <= m; i++) { for(int j = i; j <= n; j++) { dp[i][j] = 1000000; for(int k = i - 1; k <= j - 1; k++) { int tmp = 0; if(k + 1 == j) { dp[i][j] = min(dp[i][j], dp[i - 1][k]); continue; } int mid = (j + k + 1) >> 1; tmp = min(sum[mid][j] - sum[mid][k], sum[mid + 1][j] - sum[mid + 1][k]); dp[i][j] = min(dp[i][j], dp[i - 1][k] + tmp); } } } printf("%d ", dp[m][n]); } return 0; }