题意: 有 p 个水手和一个章鱼,章鱼有 n 个脚,知道了所有单位的坐标,和船长以及船员的速度,船长想去攻击章鱼的头部,但是只有在章鱼所有的脚都被水手控制的情况下才会开始朝章鱼头部进攻,问如何分配水手控制的触须,才能在最短时间内是船长攻击到其头部。
分析: 二分枚举船员移动的时间ti, 如果水手与某个触须接触所要的时间小于 ti,就在该水手与该触须之间连一条边,找出满足水手与触须最大匹配等于 n 的最小时间,最后加上船长移动到章鱼头部需要的时间即为答案。
#include<cstring> #include<cstdio> #include<cmath> #define clr(x)memset(x,0,sizeof(x)) int link[102]; bool v[102]; struct p { int to,next; }e[10002]; struct node { double x,y; double v; }te[102],pr[102],ca,he; int tot; int head[102]; void add(int s,int u) { e[tot].to=u; e[tot].next=head[s]; head[s]=tot++; } int find(int x) { int i,k; for(i=head[x];i;i=e[i].next) { k=e[i].to; if(!v[k]) { v[k]=true; if(link[k]==0||find(link[k])) { link[k]=x; return 1; } } } return 0; } int n,p; double g[102][102]; bool ok(double ti) { int i,j; tot=1; clr(head); clr(link); for(i=1;i<=p;i++) for(j=1;j<=n;j++) if(g[i][j]<ti) add(i,j); int s=0; for(i=1;i<=p;i++) { clr(v); if(find(i)) s++; } if(s==n) return true; return false; } double res; double dis(node a,node b) { return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); } int main() { int t,i,j; double low,high,mid; scanf("%d",&t); while(t--) { scanf("%d%d",&n,&p); scanf("%lf%lf%lf",&ca.x,&ca.y,&ca.v); for(i=1;i<=p;i++) scanf("%lf%lf%lf",&pr[i].x,&pr[i].y,&pr[i].v); scanf("%lf%lf",&he.x,&he.y); for(i=1;i<=n;i++) scanf("%lf%lf",&te[i].x,&te[i].y); low=0; high=-1; for(i=1;i<=p;i++) for(j=1;j<=n;j++) { g[i][j]=dis(pr[i],te[j])/pr[i].v; if(g[i][j]>high) high=g[i][j]; } while(low<high) { mid=(low+high)/2; if(ok(mid)) high=mid-0.0000000001; else low=mid+0.0000000001; } printf("%.9lf\n",low+dis(ca,he)/ca.v); } return 0; }