标签:
题目大意:有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\n",f[n][p]); 49 }
标签:
原文地址:http://www.cnblogs.com/hefenghhhh/p/4597843.html