ybt1197;p4677 山区建小学(博客里第一道蓝题)

ybt1197;p4677 山区建小学

【题目描述】

政府在某山区修建了一条道路,恰好穿越总共m个村庄的每个村庄一次,没有回路或交叉,任意两个村庄只能通过这条路来往。已知任意两个相邻的村庄之间的距离为di(为正整数),其中,0<i<m。为了提高山区的文化素质,政府又决定从m个村中选择n个村建小学(设0<n≤m<500)。请根据给定的m、n以及所有相邻村庄的距离,选择在哪些村庄建小学,才使得所有村到最近小学的距离总和最小,计算最小值。

【输入】

第1行为m和n,其间用空格间隔

第2行为m?1 个整数,依次表示从一端到另一端的相邻村庄的距离,整数之间以空格间隔。

例如:

10 3
2 4 6 5 2 4 3 1 3

表示在10个村庄建3所学校。第1个村庄与第2个村庄距离为2,第2个村庄与第3个村庄距离为4,第3个村庄与第4个村庄距离为6,...,第9个村庄到第10个村庄的距离为3。

【输出】

各村庄到最近学校的距离之和的最小值。

【输入样例】

10 2
3 1 3 1 1 1 1 1 3

【输出样例】

18

ybt递推的最后一道题,这是一道蓝题,像我这种弱菜不看题解肯定是做不出来滴!

我们在小学的时候就学过,在数轴上有若干个点,取一点使原来的点到该点距离最小的问题

若点的个数为奇数,那么就取最中间的点;

若个数为偶数,那么在中间两个点之间以及两点连成的线段也是最短的策略。

本题要放置多个学校,所以我们要分解问题。

首先,一个基本推论:每个学校服务的村子应该是连续不断的一个区间。

证明

首先,设一个学校A服务的村庄中最靠左的和最靠右的分别为x和y,那么在A的左边,x的右边,所有的村子到A的距离都比x的要小。而且既然x被A服务,说明它到A左边学校的距离比到A的距离远,所以(x,A)区间内所有村子到A的距离都比到A左边的学校距离近。A右边则同理,所以若x,y被A服务,那么[x,y]区间内所有村子都被A服务。而A所在的村子就是第(x+y)/2个村子(上文的小学知识)

这样一来,问题就变成寻找最合适的方案将m个村子分成n个区间的问题。

这样就可以用f~i,j~来表示将i个村子分成j个区间的最小距离和。

f~i,j~的转移可以考虑j从小到大推,j每次加一就相当于在之前基础上新建一所小学,但是由于状态只存储了方案的结果,没有存储学校的具体位置,所以无法决定学校建在哪,所以就考虑在原来的边界i~1~基础上,向右将边界拓展到i~2~,这样(i~1~,i~2~]区间内所有村子都被新建的学校服务。

这个算法,i~2~就是i,新学校就是第j所,我们只要枚举i~1~,从所有可行的i~1~中选出最优的给f~i,j~赋值即可。

由于每次转移都要用到第二维为j-1时的数组值,所以j在循环外层。

另外,因为转移过程中会频繁用到任意区间内建一所学校的路程和,所以建一个数组s来预处理。

并且为了计算方便,在读入的同时用前缀和的方式存储每个村子的坐标:

a[1]=0;
for(int i=2;i<=m;i++) {
    cin>>a[i];
    a[i]+=a[i-1];
    //cout<<i<<"    "<<a[i]<<endl;
}
for(int i=1;i<=m;i++) {
    for(int l=1;l<=m-i;l++) {
        s[l][l+i]=s[l][l+i-1]+a[l+i]-a[l+(i/2)];//这个递推方程(我也懒得)证明,总之画图找一下规律就出来了
        //cout<<l<<" "<<i<<" "<<s[l][l+i]<<endl;
    }
}

处理好s数组,就可以进行转移了,根据上文的思路,写出方程:

f~i,j~=min(f~i,j~,f~k,j-1~+s~k,i~)

接下来上AC代码:

#include<iostream>
#include<cstring>
using namespace std;
long long f[505][505],a[505],s[505][505],m,n,Ans=0;//邻村距离,m个村,n个学校
int main() {
    cin>>m>>n;
    a[1]=0;
    for(int i=2;i<=m;i++) {
        cin>>a[i];
        a[i]+=a[i-1];
        //cout<<i<<"    "<<a[i]<<endl;
    }
    for(int i=1;i<=m;i++) {
        for(int l=1;l<=m-i;l++) {
            s[l][l+i]=s[l][l+i-1]+a[l+i]-a[l+(i/2)];
            //cout<<l<<" "<<i<<" "<<s[l][l+i]<<endl;
        }
    }
    memset(f,0x7f,sizeof(f));//因为后面要取最小值,所以给f数组附上较大的初始值,保证f数组在处理过程中被更新。
    for(int i=1;i<=m;i++){
        f[i][1]=s[1][i];
    }
    for(int j=2;j<=n;j++) {//枚举学校数
        for(int i=j;i<=m;i++) {//枚举村子数(不得小于学校数)
            for(int k=1;k<i;k++) {//枚举第j所学校的服务区间(前文提到,左开右闭)左边界(不得大于或等于总村子数i)
                f[i][j]=min(f[i][j],f[k][j-1]+s[k+1][i]);
                //cout<<i<<" "<<j<<"    "<<f[i][j]<<endl;
            }
        }
    }
    cout<<f[m][n]<<endl;
    return 0;
}

感谢题解的启发(只是看了思路,其实文章和推理都是自己写的)

原文地址:https://www.cnblogs.com/Wild-Donkey/p/12221297.html

时间: 2025-01-10 05:10:55

ybt1197;p4677 山区建小学(博客里第一道蓝题)的相关文章

P4677 山区建小学|区间dp

P4677 山区建小学 题目描述 政府在某山区修建了一条道路,恰好穿越总共nn个村庄的每个村庄一次,没有回路或交叉,任意两个村庄只能通过这条路来往.已知任意两个相邻的村庄之间的距离为di 为了提高山区的文化素质,政府又决定从n个村中选择m个村建小学. 请根据给定的n.m以及所有相邻村庄的距离,选择在哪些村庄建小学,才使得所有村到最近小学的距离总和最小,计算最小值. 题解:https://www.luogu.org/blog/hsfzLZH1/solution-p4677 区间dp主要是下面这几段

Ubuntu 用 apache2 搭建 web 服务器 建wordpress博客

主要有5个步骤分别是: 1: 安装mysql. 2: 安装apache2 3: 安装php5 及其组件 4: 安装wordpress 第1步:安装mysql  这步非常简单.... sudo apt-get install mysql-server 输入用户名和密码..... 测试一下....就完成了 第2步: 安装apache2 sudo apt-get install apache2 安装完成 在浏览器里输入127.0.0.1就可以看到apache2的测试页 **apache2安装好后,默认

NOI题库7624 山区建小学

7624:山区建小学 Description 政府在某山区修建了一条道路,恰好穿越总共m个村庄的每个村庄一次,没有回路或交叉,任意两个村庄只能通过这条路来往.已知任意两个相邻的村庄之间的距离为di(为正整数),其中,0 < i < m.为了提高山区的文化素质,政府又决定从m个村中选择n个村建小学(设 0 < n < = m < 500 ).请根据给定的m.n以及所有相邻村庄的距离,选择在哪些村庄建小学,才使得所有村到最近小学的距离总和最小,计算最小值. Input 第1行为m

因为空间日志不能发表那么就发表到博客里来吧.实战渗透拿湖南IP 个人计算机宽带电脑...呵呵空间有截图 这上面就不发了..当然 下面是有 键盘记录的 你们自己看记录 自己破解吧 福利 还请各位多多关注我的博客@

前言:为了测试更好的 六道轮回病毒....当然  寒龙哥 我 直接搞肉鸡了....呵呵话虽如此.也会给大家发一些福利的..额 对了下面呢?有很多值得的  信息 比如说花生壳域名等等.... 呵呵大家自己看看键盘记录吧..貌似还有新浪微博了.......算 了 今天的  例程入侵思路简单 渗透xp+提权系统权限——传马-+传毒+K杀毒.....废话不多说大家自己搞搞吧...呵呵 哦 公布一下一个xp的0day挂马漏洞! html挂马代码 ...供给大家学习利用!!!!!利用windows 进行执行

7624:山区建小学

7624:山区建小学 查看 提交 统计 提问 总时间限制: 1000ms 内存限制: 65536kB 描述 政府在某山区修建了一条道路,恰好穿越总共m个村庄的每个村庄一次,没有回路或交叉,任意两个村庄只能通过这条路来往.已知任意两个相邻的村庄之间的距离为di(为正整数),其中,0 < i < m.为了提高山区的文化素质,政府又决定从m个村中选择n个村建小学(设 0 < n < = m < 500 ).请根据给定的m.n以及所有相邻村庄的距离,选择在哪些村庄建小学,才使得所有村

Python一日一练102----创建简易博客(下)

继续改博客 接着上一篇继续改咱的博客. 母语不是英语肿么办,博客上面英语好多啊! 更改很简单. 你可以做一个快速更改来观察Django管理工具是否被翻译成你想要的语言. 仅需在settings.py添加'django.middleware.locale.LocaleMiddleware' 到MIDDLEWARE_CLASSES设置中,并确保它在'django.contrib.sessions.middleware.SessionMiddleware'之后就可以了. 建立博客的前台部分 创建模版

(1.22)很多同学博客里都说c语言不安全,不严谨,那么c语言哪里不安全,不严谨?

第二个大问题:很多同学博客里都说c语言不安全,不严谨,那么c语言哪里不安全,不严谨? 看到这个问题我就想到以前老师讲C语言会碰触到计算机本身系统内存的什么地方,但是现在计算机系统也很高级就组织它碰,不知道这是不是它不安全的一个地方.(查阅资料知道了,这是因为指针乱指(??)会碰触到硬件啊,操作系统带来不安全.) 第二点,就是C语言不是面向对象的语言,数据封装性差,数据安全性低. 第三点,就是它的语法灵活,所以缺少严谨性. 原文地址:https://www.cnblogs.com/zouzou-1

注册博客alvinlee第一天!

注册此博客,主要是为了交流学习,并以此契机,激励自己好好努力,共勉之! 注册博客alvinlee第一天!,码迷,mamicode.com

池建强 博客 Mac使用技巧 第一季

第1天: 今天推送的Mac技巧: 使用OS X,我们可以充分利用系统提供的多个Space,把不同的程序放到不同的Space,让我们的系统更有扩展性.如何增加Space呢?四指上推,在桌面的最上方会出现当前的Space,把鼠标移到Space列表的右侧,会出现一个带+号的空间,点击加号,即可增加一个Space.那么如何把某个程序固定在某个Space打开呢?在某个Space打开程序,在Dock中找到这个程序图标,鼠标长按会出现一个菜单,选项-分配给,选“这个桌面”,下次再打开这个程序,就会自动进入设定