CFGYM101915 B Ali and Wi-Fi (圆+关键点)
CFGYM101915 B Ali and Wi-Fi
Mean
给定\(n\)个圆,每个圆有一个权值\(sp\),
定义一个点的权值为从覆盖了这个点的圆里选取\(m\)个圆的权值的最大累和。
求所有选点方案中的最大累和值。
\(n<=100\)。
Sol
利用关键点的思想,观察到最大方案的选点可以选在圆心或者两个圆的交点上。
暴力取出这些点,点的数目数量级不超过\(n^2\),因为一个圆和另一个圆在不重合的前提下至多只有两个交点。
枚举这些点,然后暴力判断包含(包括边界)其的圆,取出他们的权值然后排序取出前\(m\)大的权值计算,最后取最大值即可
复杂度\(O(n^3logn)\),实际上跑不满。
Code
#include<bits/stdc++.h>
using namespace std;
/*
几何 + 关键点思想
给定若干个圆,每个圆有一个权值sp,
定义一个点的权值为从覆盖了这个点的圆里选取m个圆的最大累和
求所有选点方案中的最大累和值
*/
int t;
int n,m;
#define eps 1e-6
typedef double db;
const db pi=acos(-1);
inline int sgn(db x){//符号函数 0表示0,1表示正数,-1表示负数
if(fabs(x)<eps)return 0;
return x>0?1:-1;
}
inline int dcmp(db x,db y){//比较x和y的大小,0:相等,1为大于,-1为小于
if(fabs(x-y)<eps)return 0;
else return x<y?-1:1;
}
struct Point{
db x,y;
Point():x(),y(){}
Point(db _x,db _y):x(_x),y(_y){}
void input(){
scanf("%lf %lf",&x,&y);
}
};
typedef Point Vector;
//typedef Line Sement;
inline Vector operator+ (Vector A,Vector B){return Vector(A.x+B.x,A.y+B.y);}
inline Vector operator- (Vector A,Vector B){return Vector(A.x-B.x,A.y-B.y);}
inline Vector operator* (Vector A,db k){return Vector(A.x*k,A.y*k);}
inline Vector operator/ (Vector A,db k){return Vector(A.x/k,A.y/k);}
inline bool operator== (Point A,Point B){return dcmp(A.x,B.x)==0&&dcmp(A.y,B.y)==0;}
inline bool operator< (Point A,Point B){return A.x<B.x||(A.x==B.x&&A.y<B.y);}
inline double angle(Vector v) { return atan2(v.y, v.x); }
inline db msqrt(db x){return (x<0?0:sqrt(x));}
inline db det(Vector a,Vector b){return a.x*b.y-a.y*b.x;}// 返回两向量的叉积
inline db Cross(Vector a,Vector b){return a.x*b.y-a.y*b.x;}// 返回两向量的叉积
inline db dot(Vector a,Vector b){return a.x*b.x+a.y*b.y;}//返回两向量的点积
inline db Length(Vector A){return msqrt(dot(A,A));};//返回向量的长度
inline db Length2(Vector A){return dot(A,A);}//返回向量长度的平方
inline Vector Unit(Vector x) { return x / Length(x); } //单位向量
inline db Dist(Point A,Point B){return sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y));}
inline Vector Rotate(Vector A, db rad)//逆时针旋转rad°
{
return Vector(A.x * cos(rad) - A.y*sin(rad), A.x*sin(rad) + A.y * cos(rad));
}
struct Circle{
Point c;db r;
db spe;
Circle():c(),r(),spe(){}
Circle(Point c,db r,db spe):c(c),r(r),spe(spe){}
inline Point point(double a) //根据圆心角求点坐标 参数方程
{
return Point(c.x+cos(a)*r, c.y+sin(a)*r);
}
}c[200];
//求两个圆的交点,返回个数
//已验证
int getCircleCircleIntersection(Circle C1, Circle C2, vector<Point>& sol) {
db d = Length(C1.c - C2.c);
if(sgn(d) == 0) {
if(dcmp(C1.r,C2.r) == 0) return -1; // 重合,无穷多交点
return 0;
}
if(dcmp(C1.r + C2.r,d) < 0) return 0;//外离
if(dcmp(fabs(C1.r-C2.r),d) > 0) return 0;//内含
db rad = angle(C2.c - C1.c);
Vector v = Vector(C2.c-C1.c);
db da = acos((C1.r*C1.r + d*d - C2.r*C2.r) / (2*C1.r*d));
Point p1 = C1.c+Rotate(Unit(v)*C1.r,da), p2 =C1.c+Rotate(Unit(v)*C1.r,-da);//向量加法 OC+CP=OP P为交点
sol.push_back(p1);
//if(p1 == p2) return 1;
sol.push_back(p2);
return 2;
}
//点与圆的位置关系
inline int Point_Circle_relation(Point p,Circle C){
db dst=Dist(p,C.c);
if(dcmp(dst,C.r)<0)return 0;//0:点在园内
else if(dcmp(dst,C.r)==0)return 1;//1:点在圆上
return 2;//2:点在园外
}
bool cmp(Point a,Point b){
if(a.x==b.x){
return a.y<b.y;
}
return a.x<b.x;
}
int main(){
cin>>t;
while(t--){
cin>>n>>m;
for(int i=1;i<=n;++i){
db x,y,r,sp;
cin>>x>>y>>r>>sp;
c[i]=Circle(Point(x,y),r,sp);
}
vector<Point>kep,ins;
for(int i=1;i<=n;++i){
for(int j=1;j<=n;++j){
//if(i==j)continue;
vector<Point>vp;
int val=getCircleCircleIntersection(c[i],c[j],vp);
if(val==0||val==-1){
kep.push_back(c[i].c);
}
else if(val!=0&&val!=-1){
kep.push_back(vp[0]);
kep.push_back(vp[1]);
}
}
}
sort(kep.begin(),kep.end(),cmp);
for(int i=0;i<(int)kep.size();++i){
int pos = i;
while(pos<(int)kep.size()&&kep[pos]==kep[i]){
pos++;
}
ins.push_back(kep[i]);
i=pos-1;
}
int mx=0;
vector<int>ssp;
for(int i=0;i<(int)ins.size();++i){
ssp.clear();
for(int j=1;j<=n;++j){
if(Point_Circle_relation(ins[i],c[j])<=1){
ssp.push_back(c[j].spe);
}
}
sort(ssp.begin(), ssp.end());
int cnt=m;
int sum=0;
for(int j=(int)ssp.size()-1;j>=0&&cnt;j--){
sum+=ssp[j];
cnt--;
}
mx=max(mx,sum);
}
printf("%d\n",mx);
}
return 0;
}