POJ 1160 Post Office

题意:有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;
}

  

原文地址:https://www.cnblogs.com/Apro/p/4857157.html