思路:将所有的直线的两个端点和城市混在一起,将能直接到达的两个点连线,求一次floyd最短路径。二分枚举bag容量,然后按给的要先后占领的城市由前向后,把能到一步到达的建一条边。然后求一次最小路径覆盖,就是最少需要多少个士兵才能全部占领,跟给出的p值进行比较。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define Maxn 510
#define Maxm Maxn*Maxn
#define eps 1e-6
#define inf 100000000
using namespace std;
int match[Maxn],vi[Maxn],graphic[Maxn][Maxn],n,m,p,list[Maxn];
double dis[Maxn][Maxn];
struct Point{
double x,y;
}city[Maxn*];
struct Edge{
Point a,b;
}edge[Maxn];
void init()
{
int i,j;
memset(vi,,sizeof(vi));
memset(match,-,sizeof(match));
memset(graphic,,sizeof(graphic));
for(i=;i<=;i++)
for(j=;j<=;j++)
dis[i][j]=inf;
}
double multi(Point p0, Point p1, Point p2)//j计算差乘
{ return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y);}
double Dis(Point &a,Point &b)
{ return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));}
bool is_cross(Point s1,Point e1,Point s2,Point e2)//判断线段是否相交(非规范相交)
{
return
multi(s1,e1,s2)*multi(s1,e1,e2) < -eps &&
multi(s2,e2,s1)*multi(s2,e2,e1) < -eps;
}
int dfs(int u)
{
int i,j;
for(i=;i<=n;i++)
{
if(!vi[i]&&graphic[u][i])
{
vi[i]=;
if(match[i]==-||dfs(match[i]))
{
match[i]=u;
return ;
}
}
}
return ;
}
int maxmatch()
{
int i,j;
int ans=;
memset(match,-,sizeof(match));
for(i=;i<=n;i++)
{
memset(vi,,sizeof(vi));
if(dfs(i))
ans++;
}
return n-ans;
}
int main()
{
int t,i,j,e,k;
scanf("%d",&t);
while(t--)
{
scanf("%d%d%d",&n,&m,&p);
init();
for(i=;i<=n;i++)
scanf("%lf %lf",&city[i].x,&city[i].y);
e=n;
for(i=;i<=m;i++)
{
scanf("%lf%lf%lf%lf",&edge[i].a.x,&edge[i].a.y,&edge[i].b.x,&edge[i].b.y);
city[++e].x=edge[i].a.x,city[e].y=edge[i].a.y;city[++e].x=edge[i].b.x,city[e].y=edge[i].b.y;
}
for(i=;i<=n;i++)
scanf("%d",list+i);
int flag=;
for(i=;i<e;i++)//将能直线到达的点建边
{
for(j=i+;j<=e;j++)
{
flag=;
for(k=;k<=m;k++)
{
if(is_cross(city[i],city[j],edge[k].a,edge[k].b))
{
flag=;
break;
}
}
if(!flag)
dis[i][j]=dis[j][i]=Dis(city[i],city[j]);
}
}
double Max=;
for(k=;k<=e;k++)//求所有点的最短路径
for(i=;i<=e;i++)
for(j=;j<=e;j++)
{
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
}
for(i=;i<=n;i++)
for(j=;j<=n;j++)
if(dis[i][j]!=inf)
Max=max(Max,dis[i][j]);
double l,r,mid;
l=,r=Max;
int temp;
while(r-l>eps)//二分枚举bag容量
{
mid=(l+r)/;
memset(graphic,,sizeof(graphic));
for(i=;i<n;i++)
for(j=i+;j<=n;j++)
{
if(dis[list[i]][list[j]]<=mid)
{
graphic[list[i]][list[j]]=;
}
}
temp=maxmatch();
if(temp>p)
l=mid;
else
r=mid;
}
printf("%.2lf\n",r);
}
return ;
}