zoukankan      html  css  js  c++  java
  • Post Office IOI 2000 /// 区间DP oj24077

    题目大意:

    给定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[305];
    int s[305][305]; // s[i][j] 在i到j之间设一个邮局的最小距离
    int dp[305][35]; // dp[i][j] 前i个村落设j个邮局的最小花费
    int pre[305][35],ans[35];
    
    int main()
    {
        while(~scanf("%d%d",&n,&m)) {
    
            memset(s,0,sizeof(s));
            memset(dp,INF,sizeof(dp));
            memset(pre,0,sizeof(pre));
    
            for(int i=1;i<=n;i++) scanf("%d",&a[i]);
            for(int i=1;i<=n;i++) {
                for(int j=i+1;j<=n;j++)
                    s[i][j]=s[i][j-1]+a[j]-a[(i+j)/2];
                dp[i][1]=s[1][i]; // 顺带更新一下1~i设1个邮局的dp[][]
            }
    
            for(int i=2;i<=n;i++) // 枚举末尾
                for(int j=2;j<=i && j<=m;j++) // 枚举邮局个数
                    for(int k=j-1;k<i;k++) // 枚举 1~i 的中间点
                        if(dp[k][j-1]+s[k+1][i]<dp[i][j])
                        dp[i][j]=dp[k][j-1]+s[k+1][i], pre[i][j]=k;
    
            int x=n,y=m,ind=0;
            while(x>0&&y>0) { /// 逆推并将邮局设立位置逆序存入ans[]
                ans[ind++]=a[(x+pre[x][y]+1)/2];
                x=pre[x][y--];
            }
    
            printf("%d
    ",dp[n][m]);
            while(ind>0) { // 从最后一个输出 才是正序
                printf("%d ",ans[--ind]);
            } printf("
    ");
        }
    
        return 0;
    }
    View Code
  • 相关阅读:
    Java [Leetcode 191]Number of 1 Bits
    Java [Leetcode 235]Lowest Common Ancestor of a Binary Search Tree
    Java [Leetcode 169]Majority Element
    Java [Leetcode 171]Excel Sheet Column Number
    Java [Leetcode 217]Contains Duplicate
    Java [Leetcode 242]Valid Anagram
    Java [Leetcode 100]Same Tree
    Java [Leetcode 258]Add Digits
    Java [Leetcode 104]Maximum Depth of Binary Tree
    D365 FO财务维度
  • 原文地址:https://www.cnblogs.com/zquzjx/p/9207685.html
Copyright © 2011-2022 走看看