路径详解(内含证明)
分析
首先我们画下样例的图:
可以非常明显地看出最优路径是
其实作为一个
证明
使用数学归纳法证明,归纳命题为:最内侧路径上的点数等于
① 当
② 令当
延长内侧路径第一条边将外侧路径分为两段,则有:
由于
综上,有:
则当
综合① ② 可知命题成立。结论成立。
可以看出最短路径其实可以跑一个半平面交得出,就样例而言,所求折线
算法
朴素算法就是将所有剩下的边直接暴力跑一次半平面交,复杂度
\text{AC CODE}
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<set>
#include<bitset>
#include<map>
#include<algorithm>
#include<ctime>
#include<queue>
#include<stack>
#include<random>
#define ll long long
#define ui unsigned int
#define ull unsigned long long
#define il inline
using namespace std;
template<typename T>
il void read(T &x)
{
x=0;int f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();}
while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48); ch=getchar();}
x*=f;
}
template<typename T>
il void write(T x)
{
if(x<0) {putchar('-');x=-x;}
if(x>9) write(x/10);
putchar(x%10+'0');
}
const int maxn=1e5+10,maxm=1e6+10;
const double eps=1e-12;//注意本题卡精度
int n,m,cnt,tot,top,head[maxn],h=1,t;
double ans;
bool vis[maxn],flag;
struct Edge{
int to,next;
}edge[maxm<<1];
il void add(int u,int v)
{
edge[++cnt].next=head[u];
edge[cnt].to=v;
head[u]=cnt;
}
struct point{
double x,y;
friend il point operator + (point a,point b) {return (point){a.x+b.x,a.y+b.y};}
friend il point operator - (point a,point b) {return (point){a.x-b.x,a.y-b.y};}
friend il point operator * (point a,double t) {return (point){t*a.x,t*a.y};}
}p[maxn],a[maxn];
il double d(point a,point b) {return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));}
il double dot(point a,point b) {return a.x*b.x+a.y*b.y;}
il double csp(point a,point b) {return a.x*b.y-b.x*a.y;}
il int sign(double x) {return fabs(x)<=eps?0:(x>0?1:-1);}
struct line{
point p,v;
double ang;
friend il bool operator < (line a,line b)
{return sign(a.ang-b.ang)==0?csp(a.v-a.p,b.v-a.p)>0:sign(a.ang-b.ang)<0;}
il void getang() {ang=atan2(v.y-p.y,v.x-p.x);}
}l[maxn],q[maxn];
il point inter(line a,line b)
{
point p1=a.p,p2=b.p,v1=a.v,v2=b.v,u=p2-p1;
v1=v1-p1;v2=v2-p2;
return p2+v2*(csp(u,v1)/csp(v1,v2));
}
il bool judge(line a,line b,line c)
{
point p=inter(a,b);
return sign(csp(c.v-c.p,p-c.p))<=0;
}
int main()
{
read(n);read(m);
for(int i=1;i<=n;++i) scanf("%lf%lf",&p[n-i+1].x,&p[n-i+1].y);//个人习惯是逆时针存图
for(int i=1;i<=m;++i)
{
int u,v;read(u);read(v);
if((u==1 and v==n) or (u==n and v==1)) flag=false;
u=n-u+1;v=n-v+1;add(u,v);add(v,u);
}
if(flag) {cout<<d(p[1],p[n]); return 0;}//注意特判
for(int i=1;i<n;++i)
{
for(int j=head[i];j;j=edge[j].next)
{
int v=edge[j].to;
vis[v]=1;//处理出不合法的边
}
for(int j=n;j>i;--j)
{
if(!vis[j])
{
l[++tot].p=p[i];l[tot].v=p[j];
l[tot].getang();break;
}
}
for(int j=head[i];j;j=edge[j].next) vis[edge[j].to]=0;
}
l[++tot].p=p[n]; l[tot].v=p[1]; l[tot].getang();
sort(l+1,l+tot+1);
cnt=0;
for(int i=1;i<=tot;++i)
{
if(i==1 or l[i].ang-l[i-1].ang>0) ++cnt;
l[cnt]=l[i];
}
q[++t]=l[1];
for(int i=2;i<=cnt;++i)
{
while(t>h and judge(q[t-1],q[t],l[i])) --t;
while(t>h and judge(q[h],q[h+1],l[i])) ++h;
q[++t]=l[i];
}
while(t>h and judge(q[t-1],q[t],q[h])) --t;
while(t>h and judge(q[h],q[h+1],q[t])) ++h;
tot=0;
q[t+1]=q[h];
for(int i=h;i<=t;++i) a[++tot]=inter(q[i],q[i+1]);
a[tot+1]=a[1];
for(int i=1;i<=tot;++i) ans+=d(a[i],a[i+1]);
ans-=d(p[1],p[n]);
printf("%.10lf",ans);
return 0;
}