本专题的内容主要就是学会如何用模板了。整理了一下午的半平面交模板(参考kuangbin聚聚的代码),感觉还是挺不错的。
注:该模板存点都是以逆时针存的,也就是半平面交为所有边的左边的相交的平面。
F - Feng Shui
把所有的向量往内部平移r,再枚举端点求最远距离的两个点即可。
//计算几何半平面交逆时针模板
//读点的时候要逆时针
#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
#include<math.h>
using namespace std;
const double eps=1e-8;
const double PI=acos(-1.0);
const int maxn=105;
int sgn(double x)
{
if(fabs(x)<eps)return 0;
if(x<0)return -1;
return 1;
}
struct Point
{
double x,y;
Point() {}
Point(double sx,double sy)
{
x=sx;
y=sy;
}
Point operator-(const Point &b)const
{
return Point(x-b.x,y-b.y);
}
double operator^(const Point &b)const//叉积
{
return x*b.y-y*b.x;
}
double operator *(const Point &b)const
{
return x*b.x+y*b.y;
}
};
struct Line
{
Point s,e;
double k;
Line() {}
Line(Point _s,Point _e)
{
s=_s;
e=_e;
k=atan2(e.y-s.y,e.x-s.x);
}
Point operator&(const Line &b)const
{
Point res=s;
double t=((s-b.s)^(b.s-b.e))/((s-e)^(b.s-b.e));
res.x+=(e.x-s.x)*t;
res.y+=(e.y-s.y)*t;
return res;
}
};
//
bool HPIcmp(Line a,Line b)
{
if(fabs(a.k-b.k)>eps)return a.k<b.k;
return ((a.s-b.s)^(b.e-b.s))<0;
}
Line Q[110];
void HPI(Line line[],int n,Point res[],int &resn)
{
int tot=n;
sort(line,line+n,HPIcmp);
tot=1;
for(int i=1; i<n; i++)
if(fabs(line[i].k-line[i-1].k)>eps)
line[tot++]=line[i];
int head=0,tail=1;
Q[0]=line[0];
Q[1]=line[1];
resn=0;
for(int i=2; i<tot; i++)
{
if(fabs((Q[tail].e-Q[tail].s)^(Q[tail-1].e-Q[tail-1].s))<eps||
fabs((Q[head].e-Q[head].s)^(Q[head+1].e-Q[head+1].s))<eps)
return;
while(head<tail&&(((Q[tail]&Q[tail-1])-line[i].s)^(line[i].e-line[i].s))>eps)
tail--;
while(head<tail&&(((Q[head]&Q[head+1])-line[i].s)^(line[i].e-line[i].s))>eps)
head++;
Q[++tail]=line[i];
}
while(head<tail&&(((Q[tail]&Q[tail-1])-Q[head].s)^(Q[head].e-Q[head].s))>eps)
tail--;
while(head<tail&&(((Q[head]&Q[head+1])-Q[tail].s)^(Q[tail].e-Q[tail].s))>eps)
head++;
if(tail<=head+1)return;
for(int i=head; i<tail; i++)
res[resn++]=Q[i]&Q[i+1];
if(head<tail-1)
res[resn++]=Q[head]&Q[tail];
}
Line lines[maxn];
Point p[maxn];
Point res[maxn];
int resn;
//
double dist(Point a,Point b)
{
return sqrt((a-b)*(a-b));
}
void change(Point a,Point b,Point &c,Point &d,double p)//向向量的左侧平移
{
double len=dist(a,b);
double dx=(a.y-b.y)*p/len;
double dy=(b.x-a.x)*p/len;
c.x=a.x+dx;c.y=a.y+dy;
d.x=b.x+dx;d.y=b.y+dy;
}
int main()
{
int n,r;
scanf("%d %d",&n,&r);
for(int i=n-1;i>=0;i--)
scanf("%lf %lf",&p[i].x,&p[i].y);
Point c,d;
for(int i=0;i<n;i++)
{
change(p[i],p[(i+1)%n],c,d,r);
lines[i]=Line(c,d);
}
HPI(lines,n,res,resn);
//cout<<resn<<endl;
double dd=-1;
int idx1,idx2;
for(int i=0;i<resn;i++)
for(int j=i;j<resn;j++)
{
double tmp=dist(res[i],res[j]);
if(dd<tmp)
{
dd=tmp;
idx1=i;
idx2=j;
}
}
printf("%.4f %.4f %.4f %.4f\n",res[idx1].x,res[idx1].y,res[idx2].x,res[idx2].y);
return 0;
}
G - Triathlon
一开始简单的认为随便判断一下就行。。不出意料的wa了。
正解就是半平面交,对于一个人能否得第一建不等式组,用半平面交解出解集的平面。
如何建立不等式组呢?
设一个人的三个值为V1,V2,V3,另一个人的值为U1,U2,U3,如果第一个人想得第一,那么必须满足
(1/U1−1/V1)∗x+(1/U2−1/V2)∗y+(1/U3−1/V3)∗z>0
(
1
/
U
1
−
1
/
V
1
)
∗
x
+
(
1
/
U
2
−
1
/
V
2
)
∗
y
+
(
1
/
U
3
−
1
/
V
3
)
∗
z
>
0
,由于z>0,那么我们可以除以z,将3维变成2维计算。
在建立向量的时候注意方向。方向如图所示。
//计算几何半平面交逆时针模板
//读点的时候要逆时针
#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
#include<math.h>
using namespace std;
const double eps=1e-16;
const double PI=acos(-1.0);
const double inf=1e9;
const int maxn=105;
int sgn(double x)
{
if(fabs(x)<eps)return 0;
if(x<0)return -1;
return 1;
}
struct Point
{
double x,y;
Point() {}
Point(double sx,double sy)
{
x=sx;
y=sy;
}
Point operator-(const Point &b)const
{
return Point(x-b.x,y-b.y);
}
double operator^(const Point &b)const//叉积
{
return x*b.y-y*b.x;
}
double operator *(const Point &b)const
{
return x*b.x+y*b.y;
}
};
struct Line
{
Point s,e;
double k;
Line() {}
Line(Point _s,Point _e)
{
s=_s;
e=_e;
k=atan2(e.y-s.y,e.x-s.x);
}
Point operator&(const Line &b)const
{
Point res=s;
double t=((s-b.s)^(b.s-b.e))/((s-e)^(b.s-b.e));
res.x+=(e.x-s.x)*t;
res.y+=(e.y-s.y)*t;
return res;
}
};
//
bool HPIcmp(Line a,Line b)
{
if(fabs(a.k-b.k)>eps)return a.k<b.k;
return ((a.s-b.s)^(b.e-b.s))<0;
}
Line Q[110];
void HPI(Line line[],int n,Point res[],int &resn)
{
int tot=n;
sort(line,line+n,HPIcmp);
tot=1;
for(int i=1; i<n; i++)
if(fabs(line[i].k-line[i-1].k)>eps)
line[tot++]=line[i];
int head=0,tail=1;
Q[0]=line[0];
Q[1]=line[1];
resn=0;
for(int i=2; i<tot; i++)
{
if(fabs((Q[tail].e-Q[tail].s)^(Q[tail-1].e-Q[tail-1].s))<eps||
fabs((Q[head].e-Q[head].s)^(Q[head+1].e-Q[head+1].s))<eps)
return;
while(head<tail&&(((Q[tail]&Q[tail-1])-line[i].s)^(line[i].e-line[i].s))>eps)
tail--;
while(head<tail&&(((Q[head]&Q[head+1])-line[i].s)^(line[i].e-line[i].s))>eps)
head++;
Q[++tail]=line[i];
}
while(head<tail&&(((Q[tail]&Q[tail-1])-Q[head].s)^(Q[head].e-Q[head].s))>eps)
tail--;
while(head<tail&&(((Q[head]&Q[head+1])-Q[tail].s)^(Q[tail].e-Q[tail].s))>eps)
head++;
if(tail<=head+1)return;
for(int i=head; i<tail; i++)
res[resn++]=Q[i]&Q[i+1];
if(head<tail-1)
res[resn++]=Q[head]&Q[tail];
}
Line lines[maxn];
Point p[maxn];
Point res[maxn];
int resn;
//
double dist(Point a,Point b)
{
return sqrt((a-b)*(a-b));
}
void change(Point a,Point b,Point &c,Point &d,double p)//向平面内平移
{
double len=dist(a,b);
double dx=(a.y-b.y)*p/len;
double dy=(b.x-a.x)*p/len;
c.x=a.x+dx;
c.y=a.y+dy;
d.x=b.x+dx;
d.y=b.y+dy;
}
int V[105],U[105],W[105];
int n;
bool check(int idx)
{
resn=0;
int num=0;
lines[num++]=Line(Point(eps,eps),Point(inf,eps));
lines[num++]=Line(Point(inf,eps),Point(inf,inf));
lines[num++]=Line(Point(inf,inf),Point(eps,inf));
lines[num++]=Line(Point(eps,inf),Point(eps,eps));
double x1,y1,x2,y2;
for(int i=1; i<=n; i++)if(i!=idx)
{
if(V[i]>=V[idx]&&U[i]>=U[idx]&&W[i]>=W[idx])return 0;
double a=-1.0/V[idx]+1.0/V[i];
double b=-1.0/U[idx]+1.0/U[i];
double c=-1.0/W[idx]+1.0/W[i];
int d1=sgn(a);
int d2=sgn(b);
int d3=sgn(c);
if(!d1)
{
if(!d2)continue;
x1=0;
x2=d2;
y1=y2=-c/b;
}
else
{
if(!d2)
{
x1=x2=-c/a;
y1=0;
y2=-d1;
}
else
{
x1=0;y1=-c/b;
x2=d2;
y2=-(c+a*x2)/b;
}
}
lines[num++]=Line(Point(x1,y1),Point(x2,y2));
}
HPI(lines,num,res,resn);
if(resn==0)return 0;
return 1;
}
int main()
{
scanf("%d",&n);
{
for(int i=1; i<=n; i++)
{
scanf("%d %d %d",&V[i],&U[i],&W[i]);
}
for(int i=1; i<=n; i++)
if(check(i))
puts("Yes");
else
puts("No");
}
return 0;
}
J - Nice Milk
dfs状压枚举一下选择的边,求半平面交的平面的最小值即可。
注意小细节,k有可能大于n。
//计算几何半平面交逆时针模板
//读点的时候要逆时针
#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
#include<math.h>
using namespace std;
const double eps=1e-7;
const double PI=acos(-1.0);
const double inf=1e9;
const int maxn=30;
const int maxs=1100000;
int sgn(double x)
{
if(fabs(x)<eps)return 0;
if(x<0)return -1;
return 1;
}
struct Point
{
double x,y;
Point() {}
Point(double sx,double sy)
{
x=sx;
y=sy;
}
Point operator-(const Point &b)const
{
return Point(x-b.x,y-b.y);
}
Point operator+(const Point &b)const
{
return Point(x+b.x,y+b.y);
}
Point operator/(const double b)const
{
return Point(x/b,y/b);
}
double operator^(const Point &b)const//叉积
{
return x*b.y-y*b.x;
}
double operator *(const Point &b)const
{
return x*b.x+y*b.y;
}
};
struct Line
{
Point s,e;
double k;
Line() {}
Line(Point _s,Point _e)
{
s=_s;
e=_e;
k=atan2(e.y-s.y,e.x-s.x);
}
Point operator&(const Line &b)const
{
Point res=s;
double t=((s-b.s)^(b.s-b.e))/((s-e)^(b.s-b.e));
res.x+=(e.x-s.x)*t;
res.y+=(e.y-s.y)*t;
return res;
}
void out()
{
printf("%lf %lf %lf %lf\n",s.x,s.y,e.x,e.y);
}
};
//
bool HPIcmp(Line a,Line b)
{
if(fabs(a.k-b.k)>eps)return a.k<b.k;
return ((a.s-b.s)^(b.e-b.s))<0;
}
Line Q[maxn];
void HPI(Line line[],int n,Point res[],int &resn)
{
//for(int i=0;i<n;i++)line[i].out();
int tot=n;
sort(line,line+n,HPIcmp);
//for(int i=0;i<n;i++)line[i].out();
tot=1;
for(int i=1; i<n; i++)
if(fabs(line[i].k-line[i-1].k)>eps)
line[tot++]=line[i];
int head=0,tail=1;
Q[0]=line[0];
Q[1]=line[1];
resn=0;
for(int i=2; i<tot; i++)
{
if(fabs((Q[tail].e-Q[tail].s)^(Q[tail-1].e-Q[tail-1].s))<eps||
fabs((Q[head].e-Q[head].s)^(Q[head+1].e-Q[head+1].s))<eps)
return;
while(head<tail&&(((Q[tail]&Q[tail-1])-line[i].s)^(line[i].e-line[i].s))>eps)
tail--;
while(head<tail&&(((Q[head]&Q[head+1])-line[i].s)^(line[i].e-line[i].s))>eps)
head++;
Q[++tail]=line[i];
}
while(head<tail&&(((Q[tail]&Q[tail-1])-Q[head].s)^(Q[head].e-Q[head].s))>eps)
tail--;
while(head<tail&&(((Q[head]&Q[head+1])-Q[tail].s)^(Q[tail].e-Q[tail].s))>eps)
head++;
if(tail<=head+1)return;
for(int i=head; i<tail; i++)
res[resn++]=Q[i]&Q[i+1];
if(head<tail-1)
res[resn++]=Q[head]&Q[tail];
}
Line lines[maxn];
Point res[maxn];
int resn;
//
double dist(Point a,Point b)
{
return sqrt((a-b)*(a-b));
}
void change(Point a,Point b,Point &c,Point &d,double p)//向向量ab的左侧移动
{
double len=dist(a,b);
double dx=(a.y-b.y)*p/len;
double dy=(b.x-a.x)*p/len;
c.x=a.x+dx;
c.y=a.y+dy;
d.x=b.x+dx;
d.y=b.y+dy;
}
double Area()
{
double S=0;
for(int i=1;i<resn-1;i++)
S+=(((res[i]-res[0])^(res[i+1]-res[0]))/2.0);
return S;
}
double dp[maxs];
Line L1[maxn],L2[maxn];
Point P[maxn];
int n,k,h;
double Cal(int sta)
{
int num=0;
for(int i=0;i<n;i++)
{
if((sta>>i)&1)
lines[num++]=L2[i];
else
lines[num++]=L1[i];
}
HPI(lines,num,res,resn);
return Area();
}
double dfs(int sta,int idx,int d)
{
if(d==k)
return Cal(sta);
if(n-idx<k-d)return inf;
if(dp[sta]!=inf)return dp[sta];
double res=inf;
for(int i=idx;i<n;i++)
res=min(res,dfs(sta+(1<<i),i+1,d+1));
return dp[sta]=res;
}
int main()
{
while(~scanf("%d %d %d",&n,&k,&h)&&n)
{
k=min(k,n);
for(int i=0;i<maxs;i++)dp[i]=inf;
for(int i=0;i<n;i++)
scanf("%lf %lf",&P[i].x,&P[i].y);
Point c,d;
for(int i=0;i<n;i++)
{
L1[i]=Line(P[i],P[(i+1)%n]);
change(P[i],P[(i+1)%n],c,d,h);
L2[i]=Line(c,d);
}
for(int i=0;i<n;i++)
lines[i]=L1[i];
HPI(lines,n,res,resn);
double S=Area();
printf("%.2f\n",S-dfs(0,0,0));
}
}
K - Joining with Friend
这题让我想起了高中的时候。怀念高中的时光,终究回不去了。。
用横坐标表示一个人的时间,纵坐标表示另一个人的时间,用他们时间的范和一条y-x<=w,一条x-y<=w来建立半平面交,再用该面积除以总面积就是概率了。
//计算几何半平面交逆时针模板
//读点的时候要逆时针
#include<iostream>
#include<algorithm>
#include<cstring>
#include<stdio.h>
#include<math.h>
using namespace std;
const double eps=1e-16;
const double PI=acos(-1.0);
const double inf=1e9;
const int maxn=20015;
int sgn(double x)
{
if(fabs(x)<eps)return 0;
if(x<0)return -1;
return 1;
}
struct Point
{
double x,y;
Point() {}
Point(double sx,double sy)
{
x=sx;
y=sy;
}
Point operator-(const Point &b)const
{
return Point(x-b.x,y-b.y);
}
Point operator+(const Point &b)const
{
return Point(x+b.x,y+b.y);
}
Point operator/(const double b)const
{
return Point(x/b,y/b);
}
double operator^(const Point &b)const//叉积
{
return x*b.y-y*b.x;
}
double operator *(const Point &b)const
{
return x*b.x+y*b.y;
}
};
struct Line
{
Point s,e;
double k;
Line() {}
Line(Point _s,Point _e)
{
s=_s;
e=_e;
k=atan2(e.y-s.y,e.x-s.x);
}
Point operator&(const Line &b)const
{
Point res=s;
double t=((s-b.s)^(b.s-b.e))/((s-e)^(b.s-b.e));
res.x+=(e.x-s.x)*t;
res.y+=(e.y-s.y)*t;
return res;
}
void out()
{
printf("%lf %lf %lf %lf\n",s.x,s.y,e.x,e.y);
}
};
//
bool HPIcmp(Line a,Line b)
{
if(fabs(a.k-b.k)>eps)return a.k<b.k;
return ((a.s-b.s)^(b.e-b.s))<0;
}
Line Q[maxn];
void HPI(Line line[],int n,Point res[],int &resn)
{
//for(int i=0;i<n;i++)line[i].out();
int tot=n;
sort(line,line+n,HPIcmp);
//for(int i=0;i<n;i++)line[i].out();
tot=1;
for(int i=1; i<n; i++)
if(fabs(line[i].k-line[i-1].k)>eps)
line[tot++]=line[i];
int head=0,tail=1;
Q[0]=line[0];
Q[1]=line[1];
resn=0;
for(int i=2; i<tot; i++)
{
if(fabs((Q[tail].e-Q[tail].s)^(Q[tail-1].e-Q[tail-1].s))<eps||
fabs((Q[head].e-Q[head].s)^(Q[head+1].e-Q[head+1].s))<eps)
return;
while(head<tail&&(((Q[tail]&Q[tail-1])-line[i].s)^(line[i].e-line[i].s))>eps)
tail--;
while(head<tail&&(((Q[head]&Q[head+1])-line[i].s)^(line[i].e-line[i].s))>eps)
head++;
Q[++tail]=line[i];
}
while(head<tail&&(((Q[tail]&Q[tail-1])-Q[head].s)^(Q[head].e-Q[head].s))>eps)
tail--;
while(head<tail&&(((Q[head]&Q[head+1])-Q[tail].s)^(Q[tail].e-Q[tail].s))>eps)
head++;
if(tail<=head+1)return;
for(int i=head; i<tail; i++)
res[resn++]=Q[i]&Q[i+1];
if(head<tail-1)
res[resn++]=Q[head]&Q[tail];
}
Line lines[maxn];
Point p[maxn];
Point res[maxn];
int resn;
//
double dist(Point a,Point b)
{
return sqrt((a-b)*(a-b));
}
void change(Point a,Point b,Point &c,Point &d,double p)//向平面内平移
{
double len=dist(a,b);
double dx=(a.y-b.y)*p/len;
double dy=(b.x-a.x)*p/len;
c.x=a.x+dx;
c.y=a.y+dy;
d.x=b.x+dx;
d.y=b.y+dy;
}
Line pbline(Line l)
{
Line ret;ret.s=(l.s+l.e)/2;
double a=l.e.x-l.s.x,b=l.e.y-l.s.y;
double c=(l.s.y-l.e.y)*ret.s.y+(l.s.x-l.e.x)*ret.s.x;
if(sgn(a))
{
ret.e.y=0;ret.e.x=-c/a;
if(!sgn(dist(ret.s,ret.e)))
ret.e.y=1e10;ret.e.x=-(c-b*ret.e.y)/a;
}
else
{
ret.e.x=0.0;ret.e.y=-c/b;
}
if(!sgn(dist(ret.s,ret.e)))
{
ret.e.x=1e10;ret.e.y=-(c-a*ret.e.x)/b;
}
return ret;
}
double Area()
{
double S=0;
for(int i=1;i<resn-1;i++)
S+=(((res[i]-res[0])^(res[i+1]-res[0]))/2.0);
return S;
}
int main()
{
int T;
scanf("%d",&T);
int cas=1;
while(T--)
{
int t1,t2,s1,s2,w;
scanf("%d %d %d %d %d",&t1,&t2,&s1,&s2,&w);
int num=0;
Point a(t1,s1),b(t2,s1),c(t2,s2),d(t1,s2);
lines[num++]=Line(a,b);
lines[num++]=Line(b,c);
lines[num++]=Line(c,d);
lines[num++]=Line(d,a);
Point tmp1(0,w),tmp2(-w,0);
lines[num++]=Line(tmp1,tmp2);
tmp1=Point(0,-w),tmp2=Point(w,0);
lines[num++]=Line(tmp1,tmp2);
HPI(lines,num,res,resn);
double S=(t2-t1)*(s2-s1);
printf("Case #%d: %.6f\n",cas++,Area()/S);
}
}
这个专题还算比较简单吧,主要是整理出自己习惯的模板。
kuangbin专题终于完结了,但专题二十一概率dp的总结还没写,主要是概率dp可能还没有入门,等入门之后会补上来的。
完结撒花~~