日记★DP★F-邮局

F-邮局

题目

时间限制: 1 Sec 内存限制: 64 MB

题目描述

一些村庄建在一条笔直的高速公路边上,我们用一条坐标轴来描述这条公路,每个村庄的坐标都是整数,没有两个村庄的坐标相同。两个村庄的距离定义为坐标之差的绝对值。我们需要在某些村庄建立邮局。使每个村庄使用与它距离最近的邮局,建立邮局的原则是:所有村庄到各自使用的邮局的距离总和最小。
数据规模:1<=村庄数<=300, 1<=邮局数<=30, 1<=村庄坐标<=10000

输入

第1行:2个空格分开的整数v(1<=v<=300),p(1<=p<=30且p<=v),分别表示村庄的数目和邮局的数目。

第2行:递增序列出了v个整数,分别表示了各村庄的位置坐标。对于每一个位置坐标x,有1<=x<=10000

输出

第1行:1个整数s,表示所有村庄到距离它最近的邮局的距离总和。

第2行:按递增序列出p个整数,分别表示所求出的每个邮局的建立位置。虽然对于同一个s,可能会有多种邮局建立的方案,但只需输出其中的一种。

样例输入

10 5
1 2 3 6 7 9 11 22 44 50

样例输出

9
2 7 22 44 50

状态定义

f[i][j] :前 i 个村庄中建 j 个邮局的最短距离和。

状态转移

首先考虑:在 i j 之间建一个邮局的最短距离和是多少?
显然要将邮局建在中间的村庄上(通过绝对值的几何意义可以证明)。

i j 中间一个邮局的最短距离和为 dis[i][j] (此数组可以初始化出来),那么 f[i][j] 肯定只能由 f[k][j1] 转移过来(我们无法得到 f[i][j] f[k][jx] ),即:在前 k 个村庄中放 j1 个邮局,然后在 k+1 i 村庄建一个邮局。

所以: f[i][j]=min(f[k][j1]+dis[k+1][i])|1k<i

边界

显然: f[i][1]=dis[1][i]

输出解

和大多数DP一样: pre[i][j] 表示 min(f[k][j1]+dis[k+1][i]) 选了哪个 k ,注意递归输出时不是输出 pos[pre[i][j]] pre[i][j] 只是代表在 pre[i][j]+1 i 之间放了一个邮局,所以应输出 pos[(pre[i][j]+i+1)/2]

代码

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

#define MAXN 300
#define INF 0x7f7f7f7f
int V,P;
int pos[MAXN+5],dis[MAXN+5][MAXN+5];
int f[MAXN+5][MAXN+5],pre[MAXN+5][MAXN+5];
bool fg=1;

void init()
{
    for(int i=1;i<=V;i++)
        for(int j=i+1;j<=V;j++)
        {
            int mid=(i+j)>>1;
            for(int k=i;k<mid;k++) dis[i][j]+=pos[mid]-pos[k];
            for(int k=mid+1;k<=j;k++) dis[i][j]+=pos[k]-pos[mid];
            //此处有O(1)的写法,请自行探索
            if(i==1) f[j][1]=dis[i][j];//顺便初始化f
        }
}

void print(int i,int j)
{
    if(i<1||j<1) return;
    print(pre[i][j],j-1);
    if(fg){printf("%d",pos[(pre[i][j]+i+1)/2]);fg=0;}//百年不遇的要处理行末空格的题
    else printf(" %d",pos[(pre[i][j]+i+1)/2]);
}

int main()
{
    scanf("%d%d",&V,&P);
    for(int i=1;i<=V;i++)
        scanf("%d",&pos[i]);
    init();
    for(int i=1;i<=V;i++)
        for(int j=2;j<=P;j++)
        {
            f[i][j]=INF;
            for(int k=1;k<i;k++)
                if(f[i][j]>f[k][j-1]+dis[k+1][i])
                {
                    f[i][j]=f[k][j-1]+dis[k+1][i];
                    pre[i][j]=k;
                }
        }
    printf("%d\n",f[V][P]);
    print(V,P);
}

猜你喜欢

转载自blog.csdn.net/c20190102/article/details/78821249