题意
有p个邮局,n个村庄,邮局只能建在村庄里,
求令所有寄信距离之和最短的值。、
SOLUTION:
首先可以n3的dp
dp i ,j 表示前 i 个地方建立 j 个邮局的最小花费,
在处理出在任意一个区间建立一个邮局是的最小花费(一个区间放一个邮局的话,放在中位数的位置最有)
在想如何优化dp
wqs 二分消掉一维
设f(x) = g(x) + kx,每次的设立一个邮局都要增加k的价值,看一下当前f(x)的取到最大时和x和题目要求p的关系
CODE:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <vector>
#include <stack>
#include <queue>
#include <functional>
const int INF=0x3f3f3f3f;
const int maxn=300+10;
const int mod=1e9+7;
const int MOD=998244353;
const double eps=1e-7;
typedef long long ll;
#define vi vector<int>
#define si set<int>
#define pii pair<int,int>
#define pi acos(-1.0)
#define pb push_back
#define mp make_pair
#define lowbit(x) (x&(-x))
#define sci(x) scanf("%d",&(x))
#define scll(x) scanf("%lld",&(x))
#define sclf(x) scanf("%lf",&(x))
#define pri(x) printf("%d",(x))
#define rep(i,j,k) for(int i=j;i<=k;++i)
#define per(i,j,k) for(int i=j;i>=k;--i)
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
//dp[i][j]代表前i个村庄 需要j个邮局的最小花费
//
int n,p,a[maxn],sum[maxn],dis[maxn][maxn];
int c[maxn];
int dp[maxn];
inline int check(int mid)
{
for(int i=1;i<=n;i++)
dp[i]=1e16;
dp[0]=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=i;j++)
{
if(dp[i]>dp[j-1] + dis[j][i] + mid)
{
dp[i]=dp[j-1] + dis[j][i] + mid;
c[i]=c[j-1]+1;
}
}
}
//for(int i=1;i<=n;i++)cout<<dp[i]<<" ";cout<<endl;
return c[n];
}
void work()
{
int l=-1e9,r=1e9;int ans=0;
while(l<=r)
{
int mid=l+r>>1;
if(check(mid)<=p)ans=mid,r=mid-1;
else l=mid+1;
// cout<<mid<<" "<<check(mid)<<endl;
}
check(ans);
long long t = dp[n] - p*ans;
cout<<t<<endl;
}
int main()
{
while(~scanf("%d%d",&n,&p))
{
rep(i,1,n)sci(a[i]);
sort(a+1,a+n+1);
sum[0]=0;
rep(i,1,n)sum[i]=sum[i-1]+a[i];
rep(i,1,n)
{
dis[i][i]=0;
rep(j,i+1,n)
{
int mid=(i+j)/2;
//画图可证明 坐标零点挪动为a[mid]的前缀和 可理解成每个相加 最后提出a[mid]
//sum[j]-sum[mid]为[mid+1,j]的求和,sum[mid-1]-sum[i-1]为[i,mid-1]的求和
dis[i][j]=(sum[j]-sum[mid])-(j-mid)*a[mid]+(mid-i)*a[mid]-(sum[mid-1]-sum[i-1]);
}
}
work();
}
return 0;
}