• poj1160 post office


    题目大意:有n个乡村,现在要建立m个邮局,邮局只能建在乡村里。现在要使每个乡村到离它最近的邮局距离的总和尽量小,求这个最小距离和。

    n<300,p<30,乡村的位置不超过10000.

    分析:这题是IOI的老题了,所以数据规模很小,朴素的DP也可以过。但作为四边形优化的题目也很不错。

    设f[i][j]表示前i个乡村设j个邮局的最小距离和。

    f[i][j]=min(f[k][j-1]+w(k+1,i)) 其中w(k+1,i)表示第i+1个乡村到第i个乡村到其中位点的距离之和。因为邮局肯定是建立这一段的中位点才能保证这一段的距离和最小。这个是很容易证明的,用反证法即可。

    k作为决策点,如果在区间[1,i]中枚举,则总的事件复杂度为O(N^3)

    但因为w(k+1,i)满足区间包含关系,同时满足平行四边形关系(证明貌似挺难)。所以f[i][j]也满足平行四边形原理。设s[i][j]表示f[i][j]的最佳决策点,则有

    s[i-1][j]<=s[i][j]<=s[i][j+1]。  (1)

    但实际上可以从常识角度直接推出上式(1)式。

    如果村子数不变,邮局数增加,则邮局的起止范围应该往两边扩张,至少应保持不变;相反,若邮局数减少,则其起止范围应该往中间收缩,至少应保存不变。

    所以s[i][j]<=s[i][j+1]。

    所以,可以直接应用这个式子1了。

    如果邮局数不变,村子数减少(最右边的村子剔除),则邮局应该往左边微调,不可能往右边移动;相反,则邮局应该往右微调。

    所以s[i-1][j]<=s[i][j].

    w(i,j)有O(1)的方法求出。我们首先预处理出所有乡村到最左边乡村的距离,并求前缀和,记为sum1,;再处理处所有乡村距离最右边的乡村的距离,并求后缀和,记为sum2。

    设【i,j】的中位点为k,则w(i,j)=sum1[j]-sum1[k]-(j-k)*(pos[k]-pos[1])+sum2[i]-sum2[k]-(k-i)*(pos[n]-pos[k])

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #define MAXN 305
     5 using namespace std;
     6 int f[MAXN][MAXN],w[MAXN][MAXN],s[MAXN][MAXN],sum1[MAXN],sum2[MAXN];
     7 int pos[MAXN],n,p;
     8 void pre()
     9 {
    10     for(int i=2;i<=n;i++)
    11         sum1[i]=sum1[i-1]+pos[i]-pos[1];
    12     for(int i=n-1;i>=1;i--)
    13         sum2[i]=sum2[i+1]+pos[n]-pos[i];
    14         for(int i=1;i<n;i++)
    15             for(int j=i+1;j<=n;j++)
    16             {
    17             int k=(i+j)/2;
    18     int res=0;
    19     res+=sum1[j]-sum1[k]-(j-k)*(pos[k]-pos[1]);
    20     res+=sum2[i]-sum2[k]-(k-i)*(pos[n]-pos[k]);
    21     w[i][j]=res;
    22         }
    23 }
    24 int main()
    25 {
    26     scanf("%d%d",&n,&p);
    27     for(int i=1;i<=n;i++)
    28         scanf("%d",&pos[i]);
    29         pre();
    30         memset(f,0x5f,sizeof f);
    31     for(int i=1;i<=n;i++)
    32         f[i][1]=w[1][i];
    33     for(int i=1;i<=n;i++)
    34         for(int j=min(i,p);j<=n;j++)
    35         {
    36             if(j>=i||j>p)s[i][j]=i;
    37             }
    38     for(int i=2;i<=n;i++)
    39         for(int j=min(p,i);j>=1;j--)
    40         {
    41             if(i==j){f[i][j]=0,s[i][j]=j-1;continue;}
    42             for(int k=s[i-1][j];k<=s[i][j+1];k++)
    43                 if(f[i][j]>f[k][j-1]+w[k+1][i])
    44                 {f[i][j]=f[k][j-1]+w[k+1][i];
    45                 s[i][j]=k;
    46                 }
    47         }
    48         printf("%d
    ",f[n][p]);
    49     }
    View Code
  • 相关阅读:
    Ignatius and the Princess II(全排列)
    中缀式变后缀式
    前缀式计算(前缀表达式)
    Mysql中的锁机制详解
    Mysql关于事务并发带来的问题
    第三方实用API接口汇总
    Mysql 性能优化Explain详解
    Mysql性能分析工具 SHOW PROFILE、 SHOW STATUS
    Mysql慢查询分析
    【数字图像处理】霍夫变换实现
  • 原文地址:https://www.cnblogs.com/hefenghhhh/p/4597843.html
Copyright © 2020-2023  润新知