hdu 6808 Go Running( 最小点覆盖+网络流 )

hdu 6808 Go Running( 最小点覆盖+网络流 )

hdu6808

必须知道的定理:

最小点覆盖:在二分图中,求最少的点集,使得每一条边至少都有端点在这个点集中。

最小点覆盖 = 最大匹配 (条件:在二分图中)

题意:在一条无限长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;
}

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值