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),这显然是极好的... ...