大意略。
有些地方参考了别人的博客。
思路:区间DP,一开始根本就没有思路,想了很久,很多方面木有想清楚啊,后来发现DP方程可以这样表示:
dis[i][j]可以表示在餐馆i~j建立仓库的最小距离,最小距离便是在(i+j)/2建立餐馆,这个可以证明的。
d[i][j] = min(d[i][j], d[i-1][k] + dis[k+1][j]);
d[i][j]表示从前j个餐馆内建立i个仓库,d[i-1][k] + dis[k+1][j]表示的是在1~k内范围建有i-1个仓库,而k+1~j的餐馆是由第i个仓库来管理的,于是也变成了在那个最优位置选餐馆的问题了,k的范围(1<=k < j)。
由于题目有序,可以用递归的方式输出路径。
#include <iostream> #include <cstdlib> #include <cstdio> #include <cstring> #include <string> #include <cmath> using namespace std; const int MAXN = 210; const int INF = 0x3f3f3f3f; int N, K; int dis[MAXN][MAXN]; int d[MAXN][MAXN]; int A[MAXN]; int fa[MAXN][MAXN]; void init() { memset(dis, 0, sizeof(dis)); memset(d, INF, sizeof(d)); memset(fa, 0, sizeof(fa)); } int read_case() { init(); scanf("%d%d", &N, &K); if(!N && !K) return 0; for(int i = 1; i <= N; i++) scanf("%d", &A[i]); for(int i = 1; i <= N; i++) { for(int j = 1; j <= N; j++) { for(int k = i; k <= j; k++) { dis[i][j] += abs(A[k] - A[(i+j)/2]); } } } return 1; } int dp() { for(int i = 1; i <= N; i++) d[1][i] = dis[1][i]; for(int i = 2; i <= K; i++) { for(int j = 1; j <= N; j++) { for(int k = 1; k < j; k++) { if(d[i][j] > d[i-1][k] + dis[k+1][j]) { d[i][j] = d[i-1][k] + dis[k+1][j]; fa[i][j] = k; } } } } return d[K][N]; } void print_ans(int i, int j) { int t = fa[i][j]; if(t != 1) print_ans(i, t); printf("Depot %d at restaurant %d serves restaurants %d to %d\n", i, (t+1+j)/2, t+1, j); } void solve() { int ans = dp(); print_ans(K, N); printf("Total distance sum = %d\n", ans); } int main() { int times = 0; while(read_case()) { printf("Chain %d\n", ++times); solve(); printf("\n"); } return 0; }