山区建小学 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均为正整数。如果再移动,增加更多。故选址在中间的村庄为最优方案。
*/
posted @ 2022-11-12 10:45  robinyqc  阅读(53)  评论(0)    收藏  举报