题目大意:给出n,m,在一条街上有n家餐馆,然后给出n家餐馆的坐标,然在可以在餐馆的基础上建设仓库m个,为了使得每家餐馆都能最快的调用食物,要求每家餐馆到距离自己最近的仓库的距离之和最小,并输出仓库所在餐馆的序号以及所服务的餐馆范围(到该仓库取货物的餐馆)
解题思路:dp[k][i]表示前i个餐馆用k个仓库服务的最优解,dis[i][j]表示有一个仓库服务i到j个仓库的距离和(用于dp时的计算)然后就有状态转移方程dp[k][i] = min(dp[k][i], dp[k - 1][j] + dis[j + 1][i]) (j的范围为k - 1~ i - 1),然后开一个rec数组记录每次分割的餐馆序号,用于后面输出方案,需要注意的是要将dp[0][i]全部置为较大的值,防止dp[1][i]的计算错误。计算dis[i][j]时,如过j
- i为偶数,那么中间两个餐馆都可以放置仓库,如果为奇数,肯定为中间的餐馆放置仓库。
PS:自己想出的题目和看题解做出来的题目成就感不是一个档次的。
#include <stdio.h> #include <string.h> #define min(a,b) (a)<(b)?(a):(b) const int N = 205; const int K = 35; const int MAX = 1 << 30; int n, m, dp[K][N], dis[N][N], sta[N], rec[K][N]; void init() { int sum[N]; memset(sum, 0, sizeof(sum)); for (int i = 1; i <= n; i++) { scanf("%d", &sta[i]); sum[i] = sum[i - 1] + sta[i]; } for (int i = 1; i <= n; i++) { for (int j = i; j <= n; j++) { int mid = (i + j) / 2; dis[i][j] = (mid - i) * sta[mid] - sum[mid - 1] + sum[i - 1]; dis[i][j] += sum[j] - sum[mid] - (j - mid) * sta[mid]; } } } int solve() { memset(dp, -1, sizeof(dp)); memset(rec, 0, sizeof(rec)); for (int i = 0; i <= n; i++) dp[0][i] = MAX; for (int k = 1; k <= m; k++) { for (int i = k; i <= n; i++) { dp[k][i] = dis[1][i]; for (int j = k - 1; j < i; j++) { if (dp[k - 1][j] + dis[j + 1][i] < dp[k][i]) { dp[k][i] = dp[k - 1][j] + dis[j + 1][i]; rec[k][i] = j; } } } } return dp[m][n]; } void put(int cas, int x, int y) { int mid = (x + y) / 2; printf("Depot %d at restaurant %d serves ", cas, mid); if (x == y) printf("restaurant %d\n", x); else printf("restaurants %d to %d\n", x, y); } void print(int k, int x) { if (k > 1) print(k - 1, rec[k][x]); put(k, rec[k][x] + 1, x); } int main() { int cas = 1; while (scanf("%d%d", &n, &m), n || m) { init(); printf("Chain %d\n", cas++); int ans = solve(); print(m, n); printf("Total distance sum = %d\n\n", ans); } return 0; }