题意:给你N个城市,M个线段障碍,P个士兵,所有城市和士兵都是建立在平面坐标系上,士兵身上都有一个一定容量的资源背包,每次占领一个城市,中间消耗的资源为移动到那个星球之间的最短距离,当然不能穿过障碍(士兵刚开始空降到一个城市的时候是不需要花费的)。一个士兵占领一个城市后可以填满背包然后继续去占领其他城市。求最少背包容量为多少,使得最后p个士兵可以占领N个城市。
题解:把平面上所有可以沿直线到达的点连边 (与所给的线段判相交)(为了求出两两之间最短路连边),然后floyd求一遍任意两点间的最短路。二分答案,对可以一步到达(即两个城市之间的距离小于等于当前答案时)的城市进行连边(注意题目给出的城市先后顺序),随后问题就转为 一个经典的最小路径覆盖了。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
using namespace std;
const int maxn=305;
const int maxm=100000;
int to[maxm],next[maxm],head[maxn],edge;
bool vis[maxn];
int match[maxn],n,p,m;
double dis[maxn][maxn];
int que[maxn];
struct point
{
int x,y;
} data[maxn];
int Direction(point pi,point pj,point pk)
{
return (pj.x-pi.x)*(pk.y-pi.y)-(pk.x-pi.x)*(pj.y-pi.y);
}
bool Segment_Intersect(point p1,point p2,point p3,point p4)
{
int d1=Direction(p3,p4,p1),d2=Direction(p3,p4,p2),d3=Direction(p1,p2,p3),d4=Direction(p1,p2,p4);
if(((d1>0&&d2<0)||(d1<0&&d2>0))&&((d3>0&&d4<0)||(d3<0&&d4>0)))
return 1;
return 0;
}
inline double Dis(int i,int j)
{
return sqrt((double)(data[i].x-data[j].x)*(data[i].x-data[j].x)+(double)(data[i].y-data[j].y)*(data[i].y-data[j].y));
}
inline void add(int u,int v)
{
to[edge]=v,next[edge]=head[u],head[u]=edge++;
}
bool find(int now)
{
int i,v;
for(i=head[now]; ~i; i=next[i])
if(!vis[v=to[i]])
{
vis[v]=true;
if(match[v]==-1||find(match[v]))
{
match[v]=now;
return true;
}
}
return false;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&n,&m,&p);
int cnt=n+2*m;
for(int i=1; i<=n; i++)
scanf("%d%d",&data[i].x,&data[i].y);
for(int j=n+1; j<=n+2*m; j++)
scanf("%d%d",&data[j].x,&data[j].y);
for(int i=1; i<=n; i++)
scanf("%d",&que[i]);
for(int i=1; i<=cnt; i++)
for(int j=i; j<=cnt; j++)
{
if(i==j) dis[i][j]=0;
else dis[i][j]=dis[j][i]=Dis(i,j);
}
for(int j=1; j<=cnt; j++)
for(int k=j+1; k<=cnt; k++)
for(int i=n+1; i<=cnt; i+=2)
{
if(dis[j][k]!=-1&&Segment_Intersect(data[i],data[i+1],data[j],data[k]))
{
dis[k][j]=dis[j][k]=-1;
break;
}
}
for(int k=1; k<=cnt; k++)
for(int i=1; i<=cnt; i++)
if(dis[i][k]!=-1)
for(int j=1; j<=cnt; j++)
if(dis[k][j]!=-1)
{
if(dis[i][j]==-1) dis[i][j]=dis[i][k]+dis[k][j];
else if(dis[i][j]>dis[i][k]+dis[k][j]) dis[i][j]=dis[i][k]+dis[k][j];
}
double ans=20000*sqrt(2.0);
double l=0.0,r=ans;
while(r-l>1e-8)
{
double mid=(l+r)/2.0;
memset(head,-1,sizeof(head));
memset(match,-1,sizeof(match));
edge=0;
for(int i=1; i<=n; i++)
for(int j=i+1; j<=n; j++)
if(dis[que[i]][que[j]]!=-1&&dis[que[i]][que[j]]<=mid)
add(que[i],que[j]+n);
int res=0;
for(int i=1; i<=n; i++)
{
memset(vis,0,sizeof(vis));
if(find(i))
res++;
}
if(n-res<=p) {ans=mid;r=mid;}
else l=mid;
}
printf("%.2f\n",ans);
}
return 0;
}