题目大意:

给定n个村庄的坐标,两个村庄之间的距离是其坐标之差的绝对值

最多能选m个村庄设立邮局,求设立邮局的地点使得各村庄与邮局距离总和最小

一,

所有的村庄看做在一条直线上

考虑三个因素:i 当前位于第几个村庄,j 已设立邮局的个数,s 村庄与邮局的距离总和

即dp数组为 dp[ i ][ j ] = s  , 区间DP,枚举中间点村庄 k

则dp[ i ][ j ]=max{ dp[ k ][ j-1 ] + s( k+1,i ) | 0<k<i}

(1~i村庄设j个邮局) 可由 (1~k村庄设j-1个邮局)+(k+1~i村庄设1个邮局) 得到

二,

再来考虑如何处理s( k,i ),即第 k+1 到 i 村庄之间设立一个邮局的最短距离

即考虑一个区间内选取哪个点可使 其他点到该点的差值的绝对值总和 是最小的

两个点以上的情况 首先可以排除首端和尾端的两个点

奇数个点的情况:

设a<b<c<d<e

1.选取b时

a   b   c   d   e

---

---

-------

----------

2.选取c时

a   b   c   d   e

------

---

---

------

可看出选取中间点更优

偶数个点的情况:

设a<b<c<d<e<f

1.选取c时

a   b   c   d   e   f

------

---

---

------

----------

2.选取d时

a   b   c   d   e   f

---------

------

---

---

------

可看出选取居中的两个点得到的结果是一样的

那么举个例子: s( a,d )=s( a,c )+d到邮局的距离,邮局最佳设立位置为(a+d)/2

就可得到 s( a,d )=s( a,c )+a[ d ]-a[ (a+d)/2 ]

则设数组s[][]保存 两点间设立一个邮局时 区间内村庄与邮局间距离总和的最小值

且 s[ i ][ j ]=s[ i ][ j-1 ] + a[ j ] - a[ (i+j)/2 ]

三,

邮局地点的记录(类似最长上升子序列的路径记录)

利用dp[i][j]的更新过程,枚举中间点k时,若其能更新当前的dp[i][j],

说明 1~i 被分为两个区间 1~k 和 k+1~i,则区间 k+1~i 的邮局设立点为 (i+k+1)/2

那么记录此时前驱为 k,即 pre[i][j]=k,即记录了上一个区间的末尾

则当dp[][]更新结束后,dp[n][m]是1~n村庄设立m个邮局的距离总和最小值

pre[n][m]中记录的是倒数第二个区间的末尾k1,邮局设立点为(n+k1+1)/2

则pre[k1][m-1]中则记录着倒数第三个区间的末尾k2,邮局设立点为(k1+k2+1)/2

则pre[k2][m-2]中则记录着倒数第四个区间的末尾k3 ,......

这样逆推下去 就可以当推到0时 代表已经过了第一个区间

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std; int n,m,a[];
int s[][]; // s[i][j] 在i到j之间设一个邮局的最小距离
int dp[][]; // dp[i][j] 前i个村落设j个邮局的最小花费
int pre[][],ans[]; int main()
{
while(~scanf("%d%d",&n,&m)) { memset(s,,sizeof(s));
memset(dp,INF,sizeof(dp));
memset(pre,,sizeof(pre)); for(int i=;i<=n;i++) scanf("%d",&a[i]);
for(int i=;i<=n;i++) {
for(int j=i+;j<=n;j++)
s[i][j]=s[i][j-]+a[j]-a[(i+j)/];
dp[i][]=s[][i]; // 顺带更新一下1~i设1个邮局的dp[][]
} for(int i=;i<=n;i++) // 枚举末尾
for(int j=;j<=i && j<=m;j++) // 枚举邮局个数
for(int k=j-;k<i;k++) // 枚举 1~i 的中间点
if(dp[k][j-]+s[k+][i]<dp[i][j])
dp[i][j]=dp[k][j-]+s[k+][i], pre[i][j]=k; int x=n,y=m,ind=;
while(x>&&y>) { /// 逆推并将邮局设立位置逆序存入ans[]
ans[ind++]=a[(x+pre[x][y]+)/];
x=pre[x][y--];
} printf("%d\n",dp[n][m]);
while(ind>) { // 从最后一个输出 才是正序
printf("%d ",ans[--ind]);
} printf("\n");
} return ;
}

Post Office IOI 2000 /// 区间DP oj24077的更多相关文章

  1. [IOI 2000]POJ 1160 Post Office

    Post Office Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 22278 Accepted: 12034 Descrip ...

  2. IOI 98 (POJ 1179)Polygon(区间DP)

    很容易想到枚举第一步切掉的边,然后再计算能够产生的最大值. 联想到区间DP,令dp[i][l][r]为第一步切掉第i条边后从第i个顶点起区间[l,r]能够生成的最大值是多少. 但是状态不好转移,因为操 ...

  3. UVALive 4987---Evacuation Plan(区间DP)

    题目链接 https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_ ...

  4. 2016 ACM/ICPC Asia Regional Shenyang Online 1009/HDU 5900 区间dp

    QSC and Master Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) ...

  5. poj 3186 Treats for the Cows(区间dp)

    Description FJ has purchased N (1 <= N <= 2000) yummy treats for the cows who get money for gi ...

  6. [poj3280]Cheapest Palindrome_区间dp

    Cheapest Palindrome poj-3280 题目大意:给出一个字符串,以及每种字符的加入代价和删除代价,求将这个字符串通过删减元素变成回文字符串的最小代价. 注释:每种字符都是小写英文字 ...

  7. hdu 4283 区间dp

    You Are the One Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)T ...

  8. POJ1179Polygon(区间dp)

    啊~~ 被dp摁在地上摩擦的人 今天做了一道区间dp的题(POJ1179Polygon) 题目: Polygon Time Limit: 1000MS   Memory Limit: 10000K T ...

  9. 区间dp(入门题)

    区间dp:顾名思义就是在区间上进行动态规划,通过合并小区间求解一段区间上的最优解. 常见模板: for(int len=1;len<n;len++){//区间长度 for(int be=1;be ...

随机推荐

  1. lua数组和数据类型转换

    一.lua数组 Lua数组大小不固定,下标是从  1开始. --数组 arr={"aaa","bbb","ccc"} --使用数值 for通 ...

  2. BZOJ 1415: [Noi2005]聪聪和可可(记忆化搜索+期望)

    传送门 解题思路 还是比较简答的一道题.首先\(bfs\)把每个点到其他点的最短路求出来,然后再记忆化搜索.记搜的时候猫的走法是确定的,搜一下老鼠走法就行了. 代码 #include<iostr ...

  3. NX二次开发-UFUN选择草图对话框UF_UI_select_sketch

    #include <uf.h> #include <uf_ui.h> UF_initialize(); //选择草图对话框 char sMessage[] = "选择 ...

  4. Spring源码由浅入深系列五 GetBean

    获取bean的过程如上图所示.下一章将继续图示讲解createBean的过程.

  5. 『BASH』——Learn BashScript from Daniel Robbins——[001-002]

    ABSTRACT: Daniel Robbins is best known as the creator of Gentoo Linux and author of many IBM develop ...

  6. LeetCode 67. Add Binary【个位补0,不必对齐】【easy】

    Given two binary strings, return their sum (also a binary string). The input strings are both non-em ...

  7. 一个很SB的方法,来开始调一个刚启动就SB的程序

    自己鼓捣的方法,累死我了... 遇到一个程序,这程序启动之后,会自动重新启动我的电脑, 我想调试它一下,但是遇到了问题, 如果我用调试器附加上去的话,电脑是不重起了,但是它启动了之后会立刻黑屏,全黑, ...

  8. 利用DNSQuery 进行DNS查询

    #include <WinSock2.h> #include <WinDNS.h> #pragma comment (lib, "Dnsapi.lib") ...

  9. Sublime Text自定制代码片段(Code Snippets)

    在编写代码的整个过程中,开发人员经常会一次又一次的改写或者重用相同的代码段,消除这种重复过程的方法之一是把我们经常用到的代码保存成代码片段(snippets),这使得我们可以方便的检索和使用它们. 为 ...

  10. Spring Boot + Mybatis + H2 database数据库

    H2 Database H2 由纯 Java 编写的开源关系数据库,可以直接嵌入到应用程序中,不受平台约束,便于测试. h2数据库特点 (1)性能.小巧 (2)同时支持网络版和嵌入式版本,另外还提供了 ...