Post Office

Post Office poj-1160

    题目大意:给你在数轴上的n个村庄,建立m个邮局,使得每一个村庄距离它最近的邮局的距离和最小,求距离最小和。

    注释:n<=300,m<=min(n,30)

      想法:一道DP题,超级有趣。变强中的我查了题解。是这样的:我们定义两个数组,分别是dp和sum。

        dp[i][j]表示从第一个村庄到第i个村庄建立j个邮局的最小和。

        sum[i][j]表示从第i个村庄到第j个村庄建立一个邮局的最小代价。

      然后,我们发现sum数组是可以预处理出来的。对于sum[i][j]来说,建立的邮局一定是i和j中间的那一个地方。可能是一个村庄,也可能是两个村庄之间。如果是两个村庄之间,显然对于sum[i][j]来说是没有影响的,但是对于sum[i][j+1]来说越靠近j+1越小,这是一定的,所以,我们可以非常简单的总结出sum[i][j]=sum[i][j-1]+pos[i]-pos[(i+j)/2]。这样,我们预处理出了sum数组,最重要的,我们期望处理dp数组。有了sum数组,我们对于dp[i][j]来讲可以枚举断点k,使得k+1到j只建立一个邮局,这样,我们就处理出了dp数组。

    最后,附上丑陋的代码... ...

 1 #include <iostream>
 2 #include <cstdio>
 3 #define N 310
 4 using namespace std;
 5 int sum[N][N],dp[N][N],pos[N];
 6 int main()
 7 {
 8     int n,m;
 9     while(~scanf("%d%d",&n,&m))
10     {
11         memset(sum,0,sizeof(sum));
12         for(int i=1;i<=n;i++)
13         {
14             scanf("%d",&pos[i]);
15         }
16         for(int i=1;i<=n;i++)
17         {
18             for(int j=i+1;j<=n;j++)
19             {
20                 sum[i][j]=sum[i][j-1]+pos[j]-pos[(i+j)/2];
21             }
22         }
23         memset(dp,0x3f,sizeof(dp));
24         for(int i=1;i<=n;i++)
25         {
26             dp[i][1]=sum[1][i];
27         }
28         for(int j=2;j<=m;j++)//如果j=1的话是sum的事情,和dp数组无关 
29         {
30             for(int i=j+1;i<=n;i++)//枚举dp的左端点 
31             {
32                 for(int k=j-1;k<i;k++)//枚举断点 显然断点是有范围的 
33                 {
34                        dp[i][j]=min(dp[i][j],dp[k][j-1]+sum[k+1][i]);
35                 }
36             }
37         }
38         printf("%d\n",dp[n][m]);
39     }
40     return 0;
41 }

    小结:类似于RMQ的思想,我们必须将小范围的枚举在外面,因为之后会用到。在这里,我倒是有一个想法,就是卡内存。因为我们发现,我所用到的dp数组只有刚刚更新过的和正在更新的,所以,我们可以将dp数组的内存除以(n/2),这显然是极好的... ...

posted @ 2018-01-28 22:08  JZYshuraK_彧  阅读(523)  评论(0编辑  收藏  举报