J - Programming Tutors ( 网络流+二分 )
题意:有n个学生需要到n个老师家里去听课(一对一的),给出n个学生和n个老师的位置,要求N个匹配中的距离最大值最小,输出这个最大距离最小值。
思路:用二分枚举答案,网络流检验合理性。比如当枚举到答案为100时,那么学生和老师距离超过100的就不要连边了。
代码:
#include <bits/stdc++.h>
using namespace std;
struct node {
int to,f,nxt;
};
struct nod {
int x,y;
}a[205],b[205];
const int maxn = 2e6+10;
node e[maxn];
int n,m,s,t;
int head[maxn];
int cur[maxn];
int dep[maxn];
int cnt;
int dist[205][205];
int dis( nod a, nod b )
{
return abs(a.x-b.x)+abs(a.y-b.y);
}
void addage( int u, int v, int f )
{
e[cnt].to = v;
e[cnt].f = f;
e[cnt].nxt = head[u];
head[u] = cnt++;
}
int bfs(int node)
{
memset(dep,0,sizeof(dep));
memcpy(cur,head,sizeof(cur)); // 复制head数组
dep[node] = 1;
queue <int> Q;
Q.push(node);
while ( !Q.empty() ) {
int x = Q.front();Q.pop();
for ( int i=head[x]; i!=-1; i=e[i].nxt ) {
int y = e[i].to,f = e[i].f;
if ( !dep[y] && f ) {
dep[y] = dep[x] + 1;
Q.push(y);
}
}
}
return dep[t]; // return dep[t] , 如果是0那么说明没有增广路了,退出while
}
int dfs( int x, int flow ) // dfs找增广路
{
if ( x==t ) {
return flow;
}
int sum = 0;
for ( int i=cur[x]; i!=-1; i=e[i].nxt ) {
cur[x] = i;
int y = e[i].to, f = e[i].f;
if ( f && dep[y]==dep[x]+1 ) {
int t = dfs(y,min(flow-sum,f)); // 优化1
sum += t;
e[i].f -= t;
e[i^1].f += t;
if ( sum==flow ) break; // 优化1
}
}
if ( sum==0 ) { // 如果sum==0,那么这个点之前没有增广路,深度清零
dep[x] = 0;
}
return sum;
}
int check( int mid )
{
memset(head,-1,sizeof(head));
cnt = 0;
s = 205,t=206;
for ( int i=0; i<n; i++ ) {
addage(s,i,1); addage(i,s,0);
addage(n+i,t,1); addage(t,n+i,0);
}
for ( int i=0; i<n; i++ ) {
for ( int j=0; j<n; j++ ) {
if ( dist[i][j]<=mid ) {
addage(i,n+j,1);
addage(n+j,i,0);
}
}
}
int ans = 0;
while ( bfs(s) ) {
ans += dfs(s,0x3f3f3f3f);
}
if ( ans==n ) return 1;
return 0;
}
int main()
{
int left=0,right=0;
cin >> n;
for ( int i=0; i<n; i++ ) scanf("%d %d",&a[i].x,&a[i].y);
for ( int i=0; i<n; i++ ) scanf("%d %d",&b[i].x,&b[i].y);
for ( int i=0; i<n; i++ ) {
for ( int j=0; j<n; j++ ) {
int len = dis(a[i],b[j]);
dist[i][j] = len;
right = max(right,len);
}
}
int ans = 0;
while ( left<=right ) {
int mid = (left+right)/2;
if ( check(mid)==1 ) {
ans = mid;
right = mid - 1;
}
else {
left = mid + 1;
}
}
cout << ans << endl;
return 0;
}