• 137.山区建小学


    4203 山区建小学

     

     时间限制: 1 s
     空间限制: 16000 KB
     题目等级 : 黄金 Gold
    题目描述 Description

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

    输入描述 Input Description

    第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。

    输出描述 Output Description

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

    样例输入 Sample Input

    10  2

    3

    样例输出 Sample Output

    18

    数据范围及提示 Data Size & Hint

    0 < n < = m < 500

    基本思路:

     

    
    因为不可能出现二号到四号上学,三号到一号上学的情况,即一个学校就会形成一个区间(没有重叠),于是我们用dp[m][n]表示在1至m的村庄中建立n所小学的最短路程。
    对于每一个dp[x][y]都有一种情况:在村庄x-i(x-i>=1)至村庄x的地方建立第y所小学(意思是:让村庄x-i(x-i>=1)至村庄x都到y上学),
    此时出现一个问题,究竟应该建立在(x-i)至x的哪个位置,很容易想到枚举,但有更优的方法:在村庄1-5中建一所小学,应该建在(1+5)/2的位置上,可以自己去推理,
    于是dp[x][y]=dp[x-i-1][y-1]+在(x+x-i+1)/2建小学的路程(可以不加一,只是我算路程要求我必须加),还有一个优化,比如我们算出了在x-i到x中建一所小学的路程数,
    那么x-i-1到x中建一所小学的路程数就等于x-i到x的路程数+x-i-1到本次小学的距离。
    预处理时将dp[n][m]值为无限大,但dp[1][1--m]=0,dp[2--n][1]用前面说过的一种方法推出来(等于p[1][i])。

    代码:

    #include< iostream >

    #include< cstdio >

    #include< cstdlib >

    #include< cstring >

    using namespace std;

    #define INFn 501

    #define MAX 9999999

    int f[INFn][INFn],sum[INFn],p[INFn][INFn],w[INFn];

    int m,n;

    void input()

    {

    memset(p,0,sizeof(p));

    scanf("%d%d",&m,&n);// m 村 n school

    for(int i=1;i<=m-1;++i)

    {

    scanf("%d",&w[i]);//sum统计前i条路的距离总和 

    sum[i]=sum[i-1]+w[i];

    }

    for(int i=1;i<=m-1;++i)

     for(int j=i+1;j<=m;++j)

     {

      int mid=(i+j)/2;

      for(int l=i;l<=j;++l)

      p[i][j]+=abs(sum[l-1]-sum[mid-1]); 

     }

    memset(f,99,sizeof(f));

    for(int i=1;i<=m;++i)//f数组储存着在前i个村庄建立一个学校的最小距离,先赋值为最大数 

    f[i][1]=p[1][i];

    }

    void dp()

    {

    for(int j=2;j<=n;++j)//要把学校的循环放在外面,因为会用到i-1,和j-1所以是正序循环 

     for(int i=j;i<=m;++i)//前i个村至少建立j个学校才可以 

    for(int k=j-1;k<=i-1;++k)

    f[i][j]=min(f[i][j],f[k][j-1]+p[k+1][i]);

     

    }

    int main()

    {

    input();

    dp();

    cout<<f[m][n];//f[m][n]就是最小距离 

    return 0;

     

    }


  • 相关阅读:
    某个周六加班日的划水记
    如何保证消息的可靠性传输
    PHP面向对象学习六 多态
    PHP面向对象学习五 类中接口的应用
    PHP面向对象学习四 类的关键字
    PHP面向对象学习三 类的抽象方法和类
    PHP面向对象学习二
    PHP面向对象学习一
    高级ql
    mysql 方法
  • 原文地址:https://www.cnblogs.com/c1299401227/p/5370677.html
Copyright © 2020-2023  润新知