题目链接
以x与t为轴建立直角坐标系,可以发现向右跑的同一个人在 x=t+b这条直线上,向左跑的同一个人在x=-t+b这条直线线上,肯定是让同一条直线上人算做一个人才会最少,但是直线的交点怎么算是个问题。当时还真想不到怎么建图跑网络流太菜了。
对于每个点都会有一个 xi+ti 和一个 xi-ti,在他们之间连一条有向边,最终就会建成一个二分图,问题就变成了求最小点覆盖。对于有向图最小点覆盖等于最大匹配,对于无向图还要除以二。再建造一个超级源点和一个超级汇点可以跑网络流了。xi和ti的值太大还要离散化一下。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1000010,inf=0x3f3f3f3f;
int _,s,t,n,m,h[N],e[N],ne[N],w[N],idx;
int dep[N],cur[N];
bool st[N];
int cnt,cnt2;
unordered_map<ll,int> mp1,mp2;
void add(int a,int b,int c)
{
e[idx]=b;ne[idx]=h[a];w[idx]=c;h[a]=idx++;
e[idx]=a;ne[idx]=h[b];w[idx]=0;h[b]=idx++;
}
bool bfs()
{
for(int i=0;i<=cnt;i++) dep[i]=inf;
dep[t]=inf;
dep[s]=1;
queue<int> q;
q.push(s);
while(q.size())
{
int u=q.front();
q.pop();
for(int i=h[u];i!=-1;i=ne[i])
{
int v=e[i];
if(dep[v]==inf&&w[i]>0)
{
dep[v]=dep[u]+1;
q.push(v);
}
}
}
return dep[t]!=inf;
}
int dfs(int u,int flow)
{
if(u==t) return flow;
int sum=0;
for(int i=cur[u];i!=-1;i=ne[i])
{
cur[u]=i;
int v=e[i];
if(dep[v]==dep[u]+1&&w[i]>0)
{
int tmp=dfs(v,min(flow,w[i]));
w[i]-=tmp;
w[i^1]+=tmp;
sum+=tmp;
flow-=tmp;
if(!flow) break;
}
}
return sum;
}
int dinic()
{
int flow=0;
while(bfs())
{
for(int i=0;i<=cnt;i++) cur[i]=h[i];
cur[t]=h[t];
flow+=dfs(s,inf);
}
return flow;
}
int main()
{
scanf("%d",&_);
while(_--)
{
mp1.clear();mp2.clear();
scanf("%d",&n);
s=0;
t=2*n+1;//最多会拆出2n个点,汇点建在2n+1不影响
for(int i=0;i<=n*2+1;i++) h[i]=-1;
idx=cnt=0;
for(int i=1;i<=n;i++)
{
int a,b;
scanf("%d%d",&a,&b);
int x=a-b,y=a+b;
if(mp1.count(x)==0)
{
mp1[x]=++cnt;
add(s,mp1[x],1);
}
if(mp2.count(y)==0)
{
mp2[y]=++cnt;
add(mp2[y],t,1);
}
add(mp1[x],mp2[y],1);
}
printf("%d\n",dinic());
}
}