题目大意:
在二维平面内,给出起点和终点,你可以任意方向行走,给出n个圆(圆心x,y和半径r),它们可能相交或者相离,在圆内行走不会受到辐射。现在问你,你从起点走到终点,被辐射的最短路程是多少。
换个角度思考,如果要从A圆心走到B圆心,被辐射的路程是这两个圆的圆心距减掉它俩半径之和(肯定按照直线方向走才保证最短)。如果小于零,代表这两个圆相交,被辐射的路程也就是0。所以这道题的解法是:求出任意两个圆心之间被辐射的路程,我们可以把起点和终点看成半径为0的圆。这样求出之后,就可以在这些求得的任意两点之间辐射路程的无向图中求最短路径既是答案。
#include<stdio.h>
#include<iostream>
#include<math.h>
#include<string.h>
#include<vector>
#include<queue>
using namespace std;
const double maxn=1e19;
int vis[2000];
double dis[2000];
struct C
{
double x;
double y;
double r;
}yuan[2000];
struct Node
{
int to;
double cost;
Node(int to=0,double cost=0):to(to),cost(cost){}
};
vector<Node>V[2000];
void spfa()
{
queue<int>Q;
dis[1]=0;
Q.push(1);
vis[1]=1;
int to;
double cost;
while(!Q.empty())
{
int top=Q.front();
Q.pop();
vis[top]=0;
for(int i=0;i<V[top].size();i++)
{
to=V[top][i].to;
cost=V[top][i].cost;
if(cost+dis[top]<dis[to])
{
dis[to]=cost+dis[top];
//cout<<to<<" "<<dis[to]<<endl;
if(!vis[to])
{
vis[to]=1;
Q.push(to);
}
}
}
}
return;
}
int main()
{
double sx,sy,ex,ey;
int n;
while(~scanf("%lf%lf%lf%lf",&sx,&sy,&ex,&ey))
{
scanf("%d",&n);
for(int i=1;i<=n+2;i++)
{
V[i].clear();
dis[i]=maxn;
//cout<<dis[i]<<endl;
vis[i]=0;
}
for(int i=2;i<=n+1;i++)
{
scanf("%lf%lf%lf",&yuan[i].x,&yuan[i].y,&yuan[i].r);
}
yuan[1].r=0;
yuan[1].x=sx;
yuan[1].y=sy;
yuan[n+2].r=0;
yuan[n+2].x=ex;
yuan[n+2].y=ey;
double len;
for(int i=1;i<=n+2;i++)
{
for(int j=1;j<=n+2;j++)
{
len=sqrt((yuan[i].x-yuan[j].x)*(yuan[i].x-yuan[j].x)+(yuan[i].y-yuan[j].y)*(yuan[i].y-yuan[j].y))-(yuan[i].r+yuan[j].r);
if(len<0)len=0;//cout<<i<<" "<<j<<" "<<len<<endl;
V[i].push_back(Node(j,len));
V[j].push_back(Node(i,len));
}
}
spfa();
printf("%.9f\n",dis[n+2]);
}
}