题面:
题意:
在一个一维坐标轴上有一些人在跑步,每个人可以选定跑步开始的时间,跑步结束的时间,和跑步的方向,其中方向一旦确定则不能再更改方向。每个人的速度都是每秒 1 单位距离。
给定n次记录,其中第 i 次记录 表示在 ti 时刻在坐标 xi 处至少有一个人。
问至少有多少人参加了跑步。
题解:
(1)二分图的最小点覆盖 = 二分图的最大匹配
(2)网络流求解二分图的时间复杂度是
O
(
n
m
)
O(n\sqrt m)
O(nm)的
我突然发现我不会旋转坐标系。
我们把问题转化一下。
因为是斜率
+
1
,
−
1
+1,-1
+1,−1的直线,我们将能经过这一点
(
x
i
,
y
i
)
(xi,yi)
(xi,yi)斜率为
+
1
,
−
1
+1,-1
+1,−1的直线在
y
y
y 轴上面的截距求出来,分别为
y
i
−
x
i
,
y
i
+
x
i
yi-xi,yi+xi
yi−xi,yi+xi,连一条从
y
i
−
x
i
yi-xi
yi−xi 到
y
i
+
x
i
yi+xi
yi+xi 的边。
建图之后,目标转化为二分图的若干顶点。若选择了一个左侧点 a a a,则视为选择了 y = x + a y=x+a y=x+a这条直线,若选择了右侧点 b b b,则视为选择了 y = − x + b y=-x+b y=−x+b这条直线。
可以发现,对于每条边,都要满足它的端点种至少存在一个被选择的点。
转化为求二分图的最小点覆盖。
代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#include<queue>
#include<bitset>
#include<map>
#include<unordered_map>
#include<set>
#include<list>
#include<ctime>
#define ui unsigned int
#define ll long long
#define llu unsigned ll
#define ld long double
#define pr make_pair
#define pb push_back
#define lc (cnt<<1)
#define rc (cnt<<1|1)
#define len(x) (t[(x)].r-t[(x)].l+1)
#define tmid ((l+r)>>1)
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)>(y)?(y):(x))
using namespace std;
const int inf=0x3f3f3f3f;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const double dnf=1e18;
const int mod=1e9+7;
const double eps=1e-1;
const double pi=acos(-1.0);
const int hp=13331;
const int maxn=200100;
const int maxp=400100;
const int maxm=600100;
const int up=200000;
int head[maxn],ver[maxm],edge[maxm],nt[maxm];
int d[maxn];
int s,t,tot;
queue<int>q;
int x[maxn],y[maxn],a[maxn],b[maxn];
void init(int n)
{
for(int i=0;i<=n;i++)
head[i]=0;
tot=1;
}
void add(int x,int y,int z)
{
ver[++tot]=y,edge[tot]=z,nt[tot]=head[x],head[x]=tot;
ver[++tot]=x,edge[tot]=0,nt[tot]=head[y],head[y]=tot;
}
bool bfs(void)
{
memset(d,0,sizeof(d));
while(q.size()) q.pop();
q.push(s);
d[s]=1;
while(q.size())
{
int x=q.front();
q.pop();
for(int i=head[x];i;i=nt[i])
{
int y=ver[i];
if(edge[i]&&!d[y])
{
q.push(y);
d[y]=d[x]+1;
if(y==t) return true;
}
}
}
return false;
}
int dinic(int x,int flow)
{
if(x==t) return flow;
int rest=flow ,k;
for(int i=head[x];i&&rest;i=nt[i])
{
int y=ver[i];
if(edge[i]&&d[y]==d[x]+1)
{
k=dinic(y,min(rest,edge[i]));
if(!k) d[y]=0;
edge[i]-=k;
edge[i^1]+=k;
rest-=k;
}
}
return flow-rest;
}
int getmaxx(void)
{
int flow=0,maxflow=0;
while(bfs())
while(flow=dinic(s,inf))
maxflow+=flow;
return maxflow;
}
int main(void)
{
int tt;
scanf("%d",&tt);
while(tt--)
{
int n;
scanf("%d",&n);
init(n*2+10);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&x[i],&y[i]);
a[i]=y[i]-x[i],b[i]=y[i]+x[i];
}
sort(a+1,a+n+1);
int cnta=unique(a+1,a+n+1)-(a+1);
sort(b+1,b+n+1);
int cntb=unique(b+1,b+n+1)-(b+1);
s=cnta+cntb+1,t=s+1;
int xx,yy;
for(int i=1;i<=n;i++)
{
xx=lower_bound(a+1,a+cnta+1,y[i]-x[i])-a;
yy=lower_bound(b+1,b+cntb+1,y[i]+x[i])-b;
add(xx,yy+cnta,1);
}
for(int i=1;i<=cnta;i++)
add(s,i,1);
for(int i=cnta+1;i<=cnta+cntb;i++)
add(i,t,1);
printf("%d\n",getmaxx());
}
return 0;
}