山区建小学 LuoguP4677
这道题啊十分的有趣,其做法很神奇。详见代码:
#include<bits/stdc++.h>
using namespace std;
int n,m,dn,dp[505][505],f[505][505],d[505];
signed main(){
cin>>n>>m;
for(int i=2;i<=n;i++){
cin>>dn;
d[i]=d[i-1]+dn; // |d[i]-d[j]| 表示i-j距离
}for(int i=1;i<=n;i++)
for(int j=i;j<=n;j++){
int ad=((i+j)>>1); //一定是中点最小!这是关键
for(int k=i;k<=j;k++)
f[i][j]+=abs(d[ad]-d[k]);
//f[i][j]表示i-j中只建1个小学的最小距离和
}
for(int i=0;i<=n;i++)
for(int j=0;j<=m;j++)
dp[i][j]=2e9;
dp[0][0]=0; //dp[i][j]表示表示在前i个村庄中建立j个小学的最小距离总和
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
if(j>i){dp[i][j]=0;continue;}
for(int k=j-1;k<=i;k++)
dp[i][j]=min(dp[i][j],dp[k][j-1]+f[k+1][i]);
}
cout<<dp[n][m];
return 0;
}
/*
为什么中点最小?
首先我们知道对于中间的村庄,左边有n/2个村庄,右边也有n/2个村庄
那么当我们将小学向左侧村庄移动时,左边有n/2个村庄距离减少L(L∈N+),右边有n/2+1个增加L,总距离净增L。
同理我们将小学向右移动时,左边有n/2+1个村庄距离增加R(R∈N+),右边有n/2个村庄距离减少R,总距离净增R。
L,R均为正整数。如果再移动,增加更多。故选址在中间的村庄为最优方案。
*/

浙公网安备 33010602011771号