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
状态定义
状态转移
首先考虑:在
显然要将邮局建在中间的村庄上(通过绝对值的几何意义可以证明)。
令
所以:
边界
显然:
输出解
和大多数DP一样:
代码
#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);
}