hdu 6808 Go Running( 最小点覆盖+网络流 )
必须知道的定理:
最小点覆盖:在二分图中,求最少的点集,使得每一条边至少都有端点在这个点集中。
最小点覆盖 = 最大匹配 (条件:在二分图中)
题意:在一条无限长x轴上,上面有若干人在以1m/s的速度向右或向左匀速运动,相机抓拍了n张照片,包含两个属性x和t,表示在t时刻在x点有一个人。问在整个轴上最少有几个人。比如x=1,t=2和x=3,t=4可以是一个人。
思路:画一个x-t图像可以看出来,每个点都可以延展到y轴上表示相应的两个点。所以对于给的某个点,要么是从左上方延展过来的,要么是从左下方延展过来的。到底该怎么选才能保证人数最少?这就用到了最小点覆盖,比如说x=1,t=2,可以是x0=3或者是x0=-1来的,那么我们就构建二分图,让3连接-1,最小点覆盖一定会最优的选择这个边上的至少一个点。不会匈牙利用网络流写的。
代码:
#include <bits/stdc++.h>
using namespace std;
struct node {
int to,f,nxt;
};
const int maxn = 2e6+10;
node e[maxn];
int n,m,s,t;
int head[maxn];
int cur[maxn];
int dep[maxn];
int cnt;
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 x[maxn],y[maxn];
int a[maxn],b[maxn];
int main()
{
int T;cin>>T;
while ( T-- ) {
cin>>n;
memset(head,-1,sizeof(head));cnt = 0;
for ( int i=0; i<n; i++ ) {
int t,X;scanf("%d %d",&t,&X);
x[i] = X+t;a[i]=x[i];
y[i] = X-t;b[i]=y[i];
}
sort(a,a+n);
int cnta = unique(a,a+n)-a;
sort(b,b+n);
int cntb = unique(b,b+n)-b;
s = cnta+cntb+5; t=s+1;
for ( int i=0; i<n; i++ ) {
int pos = lower_bound(a,a+cnta,x[i])-a;
int pos2 = lower_bound(b,b+cntb,y[i])-b+cnta+2;
addage(pos,pos2,1);
addage(pos2,pos,0);
}
for ( int i=0; i<cnta; i++ ) {
addage(s,i,1);
addage(i,s,0);
}
for ( int i=0; i<cntb; i++ ) {
addage(i+cnta+2,t,1);
addage(t,i+cnta+2,0);
}
int ans = 0;
while ( bfs(s) ) { // 如果还存在增广路继续
ans += dfs(s,0x3f3f3f3f); // 加一条增广路的值
}
printf("%d\n",ans);
}
return 0;
}