二分图匹配——Luogu3033 [USACO11NOV]牛的障碍Cow Steeplechase

题面:Luogu3033
我们把横着的线段分一组,竖着的线段分一组
然后把相交的线段连边即可
这样就形成了一个二分图
然后要说一下两个东西:

  • 二分图最小点覆盖:定义一个点能够覆盖以之为端点的所有边,定义点覆盖为覆盖图所有边的点集,最小点覆盖即为包含点数尽量少的点集。
  • 二分图最大独立集:独立集是两两没有公共边的点集,最大独立集即为点数尽量多的独立集。

很明显这题要求的是二分图的最大独立集
那么如何求这个独立集呢?
这里又有两个定理:

  • 二分图的最大独立集等于二分图最小点覆盖关于全点集的补集。
  • 二分图的最大匹配集合数值上等于最小点覆盖的大小。

为什么呢,(自己yy)很好理解,首先第一个画画图就可以得出了,第二个也很显然,把所有连接最小点覆盖点集的边搞掉之后剩下的点肯定没有公共边
于是我们就可以直接Hungary或者网络流求解了
dinic:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<cstdlib>
#include<string>
#include<ctime>
#include<queue>
#include<climits>
using namespace std;
int n,m,s,t,xx[1001],xy[1001],dx[1001],dy[1001];
int dist[100001],nedge=-1,head[200001],nex[200001],p[200001],c[200001];
inline void addedge(int x,int y,int z){
    p[++nedge]=y;c[nedge]=z;nex[nedge]=head[x];head[x]=nedge;
}
inline bool bfs(int s,int t){
    queue<int>q;q.push(s);
    memset(dist,-1,sizeof dist);dist[s]=1;
    while(!q.empty()){
        int now=q.front();q.pop();
        for(int k=head[now];k>-1;k=nex[k])if(c[k]&&dist[p[k]]==-1){
            dist[p[k]]=dist[now]+1;q.push(p[k]);
        }
    }
    return dist[t]>-1?1:0;
}
inline int dfs(int x,int low){
    if(x==t)return low;
    int a,used=0;
    for(int k=head[x];k>-1;k=nex[k])if(c[k]&&dist[p[k]]==dist[x]+1){
        a=dfs(p[k],min(low-used,c[k]));
        if(a)c[k]-=a,c[k^1]+=a,used+=a;
        if(used==low)break;
    }
    if(!used)dist[x]=-1;
    return used;
}
inline int dinic(){
    int flow=0;
    while(bfs(s,t))flow+=dfs(s,1e9);
    return flow;
}
inline void check(int x,int y){//判断是否相交
    if(xx[x]==dx[x]&&xx[y]==dx[y])return;
    if(xy[x]==dy[x]&&xy[y]==dy[y])return;
    if(xx[x]==dx[x]){
        if(xy[y]>=xy[x]&&xy[y]<=dy[x]&&xx[x]>=xx[y]&&xx[x]<=dx[y])addedge(x,y,1),addedge(y,x,0);
    }else{
        if(xy[x]>=xy[y]&&xy[x]<=dy[y]&&xx[y]>=xx[x]&&xx[y]<=dx[x])addedge(y,x,1),addedge(x,y,0);
    }
}
int main()
{
    memset(p,-1,sizeof p);memset(nex,-1,sizeof nex);
    memset(c,-1,sizeof c);memset(head,-1,sizeof head);
    scanf("%d",&n);
    s=0;t=n+1;
    for(int i=1;i<=n;i++){
        scanf("%d%d%d%d",&xx[i],&xy[i],&dx[i],&dy[i]);
        if(xx[i]>dx[i])swap(xx[i],dx[i]);if(xy[i]>dy[i])swap(xy[i],dy[i]);
        if(xx[i]==dx[i])addedge(s,i,1),addedge(i,s,0);
        else if(xy[i]==dy[i])addedge(i,t,1),addedge(t,i,0);
        for(int j=1;j<i;j++)check(j,i);
    }
    printf("%d",n-dinic());
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值