Post Office
题目大意:
有几个村庄,然后准备挑几个出来建邮局,村庄的位置在一个数轴上,要使村庄到离他们最近的邮局的总距离最短,想出方案,输出最短的距离。
思路:
假设n个村庄,建k个,那么我们转换成已经建成k-1个最小了,再在剩下的选一个建立最小距离和。
用两个数组来记录:
f[i][j] :是1-i个村庄,建立了j个邮局的最小消费
w[i][j]:是i-j村庄建立一个邮局的最小消费
这里求w数组优化需要来理解一下:
如果原本i和j之间有偶数个village,假设此时post office的修建地址设置为中位数中较大的数的位置,此时新加入i后,post office的位置不发生改变,那么仍然由如下计算公式:
w[i,j] =w[i,j-1]+a[i] - a[(j+i)/2]
·如果原本i和j之间有奇数个village(即(i+j)%2==0),新加入一个j之后,仍然可以取原来的K作为中位数,此时中位数位置不变,所以
w[i,j] = w[i,j-1]+a[i] - a[(j+i)/2]
这里附图理解一下
所以优化后的 w[i,j] = w[i,j-1]+a[i] - a[(j+i)/2]
最后最后的f数组转移方程:
dp[i][j]=min(dp[i][j],dp[k][j-1]+w[k+1][i]);
AC代码:
int a[maxn];
int w[400][400],dp[400][400];
int main(){
int n = read() , m = read();
for(int i=1;i<=n;i++) cin>>a[i];//输入位置,他本身就是前缀和
for(int i=1;i<=n;i++){
for(int j=1+i;j<=n;j++){
w[i][j]=w[i][j-1]+a[j]-a[(i+j)/2];
}
}
memset(dp,0x3f3f3f3f,sizeof(dp));
for(int i=1;i<=n;i++) dp[i][i]=0,dp[i][1]=w[1][i];//当只建立一个邮局时是相等的
for(int i=1;i<=n;i++){
for(int j=2;j<=min(m,i);j++){//邮局个数
//dp[i][j]=inf;
for(int k=j;k<=i-1;k++){//枚举可转移的位置,当然比邮局个数大
dp[i][j]=min(dp[i][j],dp[k][j-1]+w[k+1][i]);
}
}
}
cout<<n<<" "<<m<<" "<<dp[n][m]<<"\n";
for(int i=1;i<=n;i++) cout<<a[i]<<" ";
}
不懂的可以直接问啊