Luogu [P1378] 油滴扩展
首先,原题在这
恩,让我们进入正题:油滴扩展——显然,这道题是个搜索题,并且是个打眼一看上去就比较恶心的搜索题。
但是,这道题细节恶心,并不代表这道题思路难。
题意大家都了解了,让我们先来把细节问题坑点解决:
1.坐标范围为【-1000,1000】,有负数怎么办?
解决方法很简单粗暴:将所有坐标加上1000就行了,然后就可视为所有坐标都为正数了。
2.让输出的答案为剩余的面积,还要四舍五入。
解决方法也很简单:先搜索求出最大覆盖面积,然后将答案 + = 0.5 ,用矩形总面积减去答案输出即可。
然后就是代码实现部分,先声明变量:
int n; //n个油滴 int X1,Y1,X2,Y2;//矩形区域 double ans;//覆盖的最大面积 int x[10],y[10];//编号为 [i] 的油滴的坐标 double r[10]; //编号为 [i] 的油滴的半径 bool b[10]; //记录此油滴是否已经扩展 //三个函数,感觉这样写可能会比 C++ 自带的快一些 inline double Max(double a,double b){ return a > b ? a : b ; } inline double Min(double a,double b){ return a < b ? a : b ; } inline int Abs(int a,int b){ if(a>b) return a-b; return b-a; }
然后就让我们进入真正的重点:搜索函数(代码里有详解哦~~)
void find(int now,double sum){//now 为将要搜第几个油滴 ,sum为现在已经扩展的面积
if(now==n+1){//都搜完了
ans=Max(ans,sum);//更新答案。
return ;
}
for(int i=1;i<=n;i++)
if(!b[i]){//若这个油滴还没被扩展
int tt=0;//临时变量
for(int j=1;j<=n;j++)//这个循环的作用是判断此油滴是否已经被其他油滴覆盖了
if(b[j]&&r[j]>=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]))){
tt=1;
b[i]=1;
find(now+1,sum);
b[i]=0;
}
if(tt==1)
continue ;
b[i]=1;
r[i]=Min(Abs(x[i],X1),Abs(x[i],X2));
r[i]=Min(r[i],Min(Abs(y[i],Y1),Abs(y[i],Y2)));//此油滴的可能半径为到边界的最短路径
for(int j=1;j<=n;j++)//根据已经扩展的油滴(j)半径来确定 此油滴(i)的最小半径
if(i!=j&&b[j]){
double d=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
r[i]=Min(r[i],d-r[j]);
}
find(now+1,sum+pi*r[i]*r[i]);//寻找下一个油滴
r[i]=0;//回溯
b[i]=0;
}
}
自此,本题重点就完啦。
下面是完整代码:
#include <algorithm> #include <iostream> #include <cstring> #include <cstdio> #include <cmath> #define pi 3.1415926 using namespace std; int n; //n个油滴 int X1,Y1,X2,Y2;//矩形区域 double ans;//覆盖的最大面积 int x[10],y[10];//编号为 [i] 的油滴的坐标 double r[10]; //编号为 [i] 的油滴的半径 bool b[10]; //记录此油滴是否已经扩展 //三个函数,感觉这样写可能会比 C++ 自带的快一些 inline double Max(double a,double b){ return a > b ? a : b ; } inline double Min(double a,double b){ return a < b ? a : b ; } inline int Abs(int a,int b){ if(a>b) return a-b; return b-a; } void find(int now,double sum){//now 为将要搜第几个油滴 ,sum为现在已经扩展的面积 if(now==n+1){//都搜完了 ans=Max(ans,sum);//更新答案。 return ; } for(int i=1;i<=n;i++) if(!b[i]){//若这个油滴还没被扩展 int tt=0;//临时变量 for(int j=1;j<=n;j++)//这个循环的作用是判断此油滴是否已经被其他油滴覆盖了 if(b[j]&&r[j]>=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]))){ tt=1; b[i]=1; find(now+1,sum); b[i]=0; } if(tt==1) continue ; b[i]=1; r[i]=Min(Abs(x[i],X1),Abs(x[i],X2)); r[i]=Min(r[i],Min(Abs(y[i],Y1),Abs(y[i],Y2)));//此油滴的可能半径为到边界的最短路径 for(int j=1;j<=n;j++)//根据已经扩展的油滴(j)半径来确定 此油滴(i)的最小半径 if(i!=j&&b[j]){ double d=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j])); r[i]=Min(r[i],d-r[j]); } find(now+1,sum+pi*r[i]*r[i]);//寻找下一个油滴 r[i]=0;//回溯 b[i]=0; } } inline int read(){ int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } int main(){ n=read(); X1=read()+1000,Y1=read()+1000,X2=read()+1000,Y2=read()+1000; for(int i=1;i<=n;i++) x[i]=read()+1000,y[i]=read()+1000; find(1,0.0); ans=Abs(X1,X2)*Abs(Y1,Y2)-ans; int answer=int(ans+0.5); printf("%d",answer); return 0; }