日记★DP★F-邮局

来源:互联网 发布:unity3d海底场景 编辑:程序博客网 时间:2024/05/16 09:34

  • 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个邮局的最短距离和。

状态转移

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

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

所以: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]+1i之间放了一个邮局,所以应输出pos[(pre[i][j]+i+1)/2]

代码

#include<cstdio>#include<algorithm>using namespace std;#define MAXN 300#define INF 0x7f7f7f7fint 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);}
原创粉丝点击