题目
给定两条平行轴上的点,两条轴的距离是s(s<=50)
第一条轴上给定m(m<=1000)个点,第二条轴上给定n(n<=1000)个点,点坐标x(0<=x<=1000)
在距两条轴距离相等且平行的线上,标记k(k<=100)个位置点,
使得n+m个点,点i对k个位置中距离最近的点算距离,记为xi
要求最小,并输出这个值,误差不超过1e-5
思路来源
gym AC代码
【学习笔记】各类基于决策单调性的dp优化_决策单调性优化dp_sophilex的博客-CSDN博客
题解
首先,由于轴两侧是等距的,可以把轴上的点合到其中一侧并排序,
d即线上修建k个点,轴上n+m个点,到线的距离为s/2,每个点到最近点的距离平方的和最小
那么每个修建的点对应的肯定是一段连续的区间,枚举最后一个修建点对应了哪一段朴素dp即可
为了确定修建点的位置,
考虑到
的距离的平方,为
是固定的,可以最后加上,考虑怎么求前一项,
即的最小值,
三分是理论可行的,但是写了一发交上去t了,原因在double还是比较耗时的
所以考虑对y求导,
有,将所求式展开,分别为
、
、
分别维护前缀和、前缀平方和,即可O(1)求得最小值
然后朴素dp即可,枚举最后一段,复杂度
优化
主要是学习一下分治优化dp,感觉本题就是一个决策单调性的典题
可将mid所在决策选点时,与其共处一个区间的最左左端点,看成是决策点
则决策点只有[1,n]这些种,套用分治优化决策单调性即可
复杂度
代码1(朴素dp)
// Problem: B. B Road Band
// Contest: Codeforces - 2023-2024 ICPC East North America Regional Contest (ECNA 2023)
// URL: https://codeforces.com/gym/104757/problem/B
// Memory Limit: 1024 MB
// Time Limit: 4000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair<ll,int> P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define scll(a) scanf("%lld",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
#define debug(...) fprintf(stderr, __VA_ARGS__)
const int K=105,N=2e3+10;
const db eps=1e-3;
int m,n,k,s;
db a[N],dis[N][N],pos[N][N],dp[K][N],sum[N],sum2[N];
db cal(int l,int r){
db mid=(sum[r]-sum[l-1])/(r-l+1);
return (sum2[r]-sum2[l-1])-2*mid*(sum[r]-sum[l-1])+(r-l+1)*mid*mid;
}
void sol(){
sci(m);sci(n),sci(k),sci(s);
rep(i,1,m)scanf("%lf",&a[i]);
rep(i,m+1,m+n)scanf("%lf",&a[i]);
m+=n;
sort(a+1,a+m+1);
for(int i=1;i<=m;i++){
sum[i]=sum[i-1]+a[i];
sum2[i]=sum2[i-1]+a[i]*a[i];
}
rep(i,1,m){
rep(j,i,m){
dis[i][j]=cal(i,j);
//printf("i:%d j:%d dis:%lf\n",i,j,dis[i][j]);
}
}
rep(i,1,m)dp[0][i]=8e18;
rep(i,1,k){
rep(j,1,m){
dp[i][j]=8e18;
rep(x,1,j){
dp[i][j]=min(dp[i][j],dp[i-1][j-x]+dis[j-x+1][j]);
}
//printf("i:%d j:%d dp:%lf\n",i,j,dp[i][j]);
}
}
db ans=dp[k][m]+m*(s/2.)*(s/2.);
printf("%.10lf\n",ans);
}
int main(){
sol();
return 0;
}
代码2(决策单调性 分治优化)
// Problem: B. B Road Band
// Contest: Codeforces - 2023-2024 ICPC East North America Regional Contest (ECNA 2023)
// URL: https://codeforces.com/gym/104757/problem/B
// Memory Limit: 1024 MB
// Time Limit: 4000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair<ll,int> P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define scll(a) scanf("%lld",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
#define debug(...) fprintf(stderr, __VA_ARGS__)
const int K=105,N=2e3+10;
const db eps=1e-3;
int m,n,k,s;
db a[N],pos[N][N],dp[N],sum[N],sum2[N],dp2[N];
db cal(int l,int r){
db mid=(sum[r]-sum[l-1])/(r-l+1);
return (sum2[r]-sum2[l-1])-2*mid*(sum[r]-sum[l-1])+(r-l+1)*mid*mid;
}
void dfs(int l,int r,int pl,int pr){
int mid=(l+r)/2,p=pl;
db res=dp[pl]+cal(pl+1,mid);
for(int j=pl+1;j<=min(mid-1,pr);j++){
double tmp=dp[j]+cal(j+1,mid);
if(tmp<res){
res=tmp;
p=j;
}
}
dp2[mid]=res;
if(l<mid)dfs(l,mid-1,pl,p);
if(mid<r)dfs(mid+1,r,p,pr);
}
void sol(){
sci(m);sci(n),sci(k),sci(s);
rep(i,1,m)scanf("%lf",&a[i]);
rep(i,m+1,m+n)scanf("%lf",&a[i]);
m+=n;
sort(a+1,a+m+1);
for(int i=1;i<=m;i++){
sum[i]=sum[i-1]+a[i];
sum2[i]=sum2[i-1]+a[i]*a[i];
}
rep(i,0,m)dp[i]=dp2[i]=8e18;
dp[0]=0;
rep(i,1,k){
dfs(1,m,0,m);
swap(dp,dp2);
}
db ans=dp[m]+m*(s/2.)*(s/2.);
printf("%.10lf\n",ans);
}
int main(){
sol();
return 0;
}