题意是给定平面内2000给点(都在y轴右边),要选出一些点,和原点组成多边形,求在多边形内的点的点权和除以多边形周长的最大值。
分数规划,二分答案
∑wi∑disi,j≥mid
∑w−mid∑disi,j≥0
这样求 ∑w−mid∑disi,j 的最大值就行了
发现答案肯定是个凸多边形
考虑DP,把点按照极角排序,令 fi 为前 i 个点选出一些点的最大值。
那么
其中
Query(i,j)
表示原点
i
和
Query(i,j) 这个东西预处理一下,就可以 n2 DP了
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <set>
#include <cmath>
#include <assert.h>
using namespace std;
const int N=2010;
const double eps=1e-8;
int n;
double f[N];
struct Pt{
int x,y,g,w; double val;
Pt(int _a=0,int _b=0):x(_a),y(_b){}
friend bool operator <(Pt a,Pt b){
return b.val>a.val;
}
friend Pt operator -(Pt a,Pt b){
return Pt(a.x-b.x,a.y-b.y);
}
double len(){
return sqrt(x*x+y*y);
}
}p[N],tmp[N];
inline double ang(Pt a,Pt b){
return acos((a.x*b.x+a.y*b.y)/a.len()/b.len());
}
inline double dist(Pt a,Pt b){
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
int pos[N],b[N],g[N][N];
inline int Query(int x,int n){
int ret=0;
for(;x<=n;x+=x&-x) ret+=b[x];
return ret;
}
inline void Add(int x,int y){
for(;x;x-=x&-x) b[x]+=y;
}
inline bool check(double x){
for(int i=1;i<=n;i++) f[i]=p[i].w-dist(p[i],Pt())*x;
for(int i=2;i<=n;i++)
for(int j=1;j<i;j++){
f[i]=max(f[i],f[j]+g[j][i]-x*dist(p[i],p[j]));
}
for(int i=1;i<=n;i++) if(f[i]-x*dist(p[i],Pt())>=0) return true;
return false;
}
inline void Pre(){
for(int i=1;i<=n;i++){
int cnt=0,tot=0;
for(int j=i+1;j<=n;j++){
tmp[++cnt]=p[j];
tmp[cnt].val=ang(p[j]-p[i],p[i]);
tmp[cnt].g=j;
}
sort(tmp+1,tmp+1+cnt);
for(int j=1;j<=cnt;j++){
pos[tmp[j].g]=j; b[j]=0;
}
for(int j=i+1;j<=n;j++){
Add(pos[j],p[j].w);
g[i][j]=Query(pos[j],cnt);
}
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].w),p[i].val=ang(p[i],Pt(0,-1));
sort(p+1,p+1+n);
Pre();
double L=0,R=1e9,mid;
while(R-L>eps){
check(mid=(L+R)/2)?L=mid:R=mid;
}
printf("%.8lf\n",R);
return 0;
}