大意:在一个二维坐标系上有nx个人和ny把伞,每个人都有自己的移动速度,问有多少人可以再tmin内移动到不同的雨伞处(不允许两个人共用一把伞)。
思路:很容易可以看出,这是一个二分图模型,雨伞和人一一对应,典型的匹配问题,而又要求最大,所以是二分最大匹配问题,再看看题目的数据量,nx:3000,ny:3000,极限情况下有9000000条边,很明显,匈牙利算法可能会TLE,所以为了降低时间复杂度,我们由每次寻找一条增广路径扩展到寻找多条增广路径,这就跟Dinic与连续增广路的关系很相似。
如何建图呢?只要满足dist(a[i], a[j]) <= si*T的点连一条边即可。
介绍一下Hopcroft-Karp算法,这种算法可以多次寻找增广路径,这样迭代的次数最多为2n^0.5,所以算法优化到了O(n^0.5*m)。
算法详解:http://ws.nju.edu.cn/courses/gt/5.pdf
注意:本模板来自kuangbin模板,该题使用邻接矩阵存的图,且二部图左右点集下标从0开始
#include<stdio.h>
#include<queue>
#include<iostream>
#include<string.h>
#include<math.h>
using namespace std;
#define eps 1e-6const int MAXN=3005;
const int INF=1<<28;
int g[MAXN][MAXN],Mx[MAXN],My[MAXN],Nx,Ny;
int dx[MAXN],dy[MAXN],dis;
bool vst[MAXN];
struct Node1
{int x,y,s;
}guests[MAXN];
struct Node2
{int x,y;
}um[MAXN];
double distance(Node1 a,Node2 b)
{double x=a.x-b.x;double y=a.y-b.y;return sqrt(x*x+y*y);
}
bool searchP()
{queue<int>Q;dis=INF;memset(dx,-1,sizeof(dx));memset(dy,-1,sizeof(dy));for(int i=0;i<Nx;i++)if(Mx[i]==-1){Q.push(i);dx[i]=0;}while(!Q.empty()){int u=Q.front();Q.pop();if(dx[u]>dis) break;for(int v=0;v<Ny;v++)if(g[u][v]&&dy[v]==-1){dy[v]=dx[u]+1;if(My[v]==-1) dis=dy[v];else{dx[My[v]]=dy[v]+1;Q.push(My[v]);}}}return dis!=INF;
}
bool DFS(int u)
{for(int v=0;v<Ny;v++)if(!vst[v]&&g[u][v]&&dy[v]==dx[u]+1){vst[v]=1;if(My[v]!=-1&&dy[v]==dis) continue;if(My[v]==-1||DFS(My[v])){My[v]=u;Mx[u]=v;return 1;}}return 0;
}
int MaxMatch()
{int res=0;memset(Mx,-1,sizeof(Mx));memset(My,-1,sizeof(My));while(searchP()){memset(vst,0,sizeof(vst));for(int i=0;i<Nx;i++)if(Mx[i]==-1&&DFS(i)) res++;}return res;
}int main()
{int n,m,t,i,j;int T,iCase=0;scanf("%d",&T);while(T--){iCase++;scanf("%d",&t);scanf("%d",&m);for(i=0;i<m;i++)scanf("%d%d%d",&guests[i].x,&guests[i].y,&guests[i].s);scanf("%d",&n);for(i=0;i<n;i++)scanf("%d%d",&um[i].x,&um[i].y);Nx=m;Ny=n;memset(g,0,sizeof(g));for(i=0;i<m;i++){for(j=0;j<n;j++){if(distance(guests[i],um[j])/guests[i].s-t<eps){g[i][j]=1;}}}printf("Scenario #%d:\n%d\n\n",iCase,MaxMatch());}return 0;
}