POJ 1160 Post Office(抽象的二维DP)

题意:

有 n 个村庄,要求选定其中 p 个建立邮局,每个村庄使用离他最近的那个邮局,求一种方案使最终的距离总和最小;

(黑书 157 邮局)

思路:

1. 题目中有 2 个“最”:最近的邮局、距离总和最小 -> 对于第一个最我们可以采取预处理的方式 cost[i, j] 即 i, j 之间建立一个邮局的最近距离;

2. 最近的邮局这个很好解决:通过规律可以发现,如果邮局建在 i, j 的中间位置一定是最优的选择。下面解决距离总和最小;

3. dp[i, j] 代表 前 i 个邮局建立在前 j 个村庄的最小距离总和。相当于是这 i 个邮局各有一片管辖区域,如何划分使总距离最小:

4. 关于区间划分,这让人想到黑书上面任务调度类似的题目,推敲下有: dp[i, j] = min(dp[i, j], dp[i-1][k] + cost[k+1, j]);

 

#include <iostream>
#include <algorithm>
using namespace std;

const int MAXN = 310;
const int INFS = 0x3fffffff;

int dp[MAXN][MAXN], cost[MAXN][MAXN], x[MAXN];
int vill, office;

int calccost(int i, int j) {
    int ans = 0;
    while (i < j) {
        ans += x[j] - x[i];
        j--, i++;
    }
    return ans;
}

int workout() {
    for (int i = 0; i <= office; i++)
        for (int j = 0; j <= vill; j++)
            dp[i][j] = INFS;
    dp[0][0] = 0;

    for (int i = 1; i <= office; i++) {
        for (int j = i; j <= vill; j++) {
            for (int k = 0; k < j; k++)
                dp[i][j] = min(dp[i][j], dp[i-1][k] + cost[k+1][j]);
        }
    }
    return dp[office][vill];
}

int main() {
    while (~scanf("%d%d", &vill, &office)) {
        x[0] = 0;
        for (int i = 1; i <= vill; i++)
            scanf("%d", &x[i]);

        for (int i = 1; i <= vill; i++)
            for (int j = i; j <= vill; j++)
                cost[i][j] = calccost(i, j);

        printf("%d\n", workout());
    }
    return 0;
}
原文地址:https://www.cnblogs.com/kedebug/p/3006520.html