Description
Input
Output
Sample Input
10 5 1 2 3 6 7 9 11 22 44 50
Sample Output
9
思路:
1. dist[i][j] 表示在村子 i, j 之间选一点建邮局并使得 i,j 之间的所有村子到该邮局的距离之和最小. 选择方法是固定的, 总是选择 i,j 最中间的那个村子, mid = (i+j+1)/2
2. dp[i][j] 表示前 i 个村子建立 j 个邮局后各个村子到邮局的总距离之和的最小值
3. dp[i][j] = dp[k][j-1] + dist[k+1][i], 类似背包, 不过这里有个枚举的过程.
为什么第 k+1 个村庄非得去 (k+1,j) 之间的那个邮局, 这应该是个枚举的过程, 强制 0~j-1 分配 k 个, j~i 分配 1 个, 枚举所有可能性
总结:
1. 四边形不等式: 状态转移方程形如:f[i] = opt{f[j]+w[j , i]} 其中b[i] <= j <= i-1, 同时满足w[i , j] + w[i' , j'] <= w[i , j'] + w[i' , j], 其中 i <= i' <= j' <= j, 则能够使用四边形优化, 减少 k 的枚举量, 加速状态转移
代码:
#include <iostream> using namespace std; const int MAXN = 310; int village[MAXN]; int dist[MAXN][MAXN]; int dp[MAXN][40]; int V, P, S; void preProcess() { for(int i = 1; i <= V; i ++) { for(int j = i; j <= V; j ++) { int mid = (i+j+1)>>1; int sum = 0; for(int k = i; k <= j; k ++) { sum += abs(village[mid]-village[k]); } dist[i][j] = sum; } } int INF = 0X3f; memset(dp, INF, sizeof(dp)); dp[0][0] = 0; } // dp[i][j] = min(dp[k][j-1] + dist[k+1][j]) int mainFunc() { for(int i = 1; i <= V; i ++) { for(int j = 1; j <= P; j ++) { for(int k = 0; k < i; k ++) { dp[i][j] = min(dp[i][j], dp[k][j-1] + dist[k+1][i]); } } } return dp[V][P]; } int main() { freopen("E:\\Copy\\ACM\\poj\\1160\\in.txt", "r", stdin); while(cin>> V >> P) { for(int i = 1; i <= V; i ++) { scanf("%d", &village[i]); } preProcess(); cout << mainFunc() << endl; } return 0; }