HDU 4619 —— Warm up 2(二分图最大匹配)

题目:http://acm.hdu.edu.cn/showproblem.php?pid=4619

最近学了二分匹配,这块我是看kuangbin大神的文章的:http://www.cnblogs.com/kuangbin/archive/2012/08/26/2657446.html

个人觉得他博客的文章很赞,很多总结也写得很好,强力推荐!

平面上有很多1X2的多米诺骨牌,横放或竖放,相同方向的不会重叠。现在要取走一些,使得剩下的两两没有重叠,问最多能剩下多少个。

有两种考虑的方法,一种是每块牌看作一个点,有重叠到的两块骨牌之间就连一条线,那么问题就是求最大独立集,最大独立集=总点数-最大匹配。

另一种就是将每个格点(每个骨牌占据两个格点)当作一个点,同一块骨牌覆盖到的格点就连一条线,那么问题就是对这些格点求一个最大匹配。

前者可以看作有N+M个点,后者最多是2*(N+M)个点。

但前者构图的时候每个骨牌都要去遍历另一种骨牌,构图的复杂度是O(NM),这种做法在kuangbin的博客上也有;

后者则只是在读入骨牌的时候直接构图,不用考虑其他骨牌,复杂度是O(N+M),但相对的后期处理的点就比较多,我采用的是这种;

对于每个坐标(x,y),因为0<=x<=100 ,0<=y<=100,所以每个坐标可以映射成一个数z=x*101+y

用一个f[z]来记录这块格点对应的编号,从1开始,因为N和M都不超过1000,所以覆盖到的最多4000个,如果不整理编号总共要101*101个点,但其中很多是空白的。

然后就是构图求最大匹配,最后的答案记得除以2。

二分匹配用的是Hopcroft-Carp算法。

#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
#define pb push_back
#define maxn 4001
const int inf = 0x7fffffff;
inline void IN(int& x){
    char c=getchar();
    while(c<48 || c>57) c=getchar();
    x=0;
    while(c>=48 && c<=57){
        x=x*10+c-48;
        c=getchar();
    }
}
vector<int> V[maxn];
int n, dis;
int f[20000];
int dx[maxn], dy[maxn], Mx[maxn],  My[maxn];
bool vis[maxn];
bool search(){
    queue<int> Q;
    dis=inf;
    memset(dx,-1,sizeof(dx));
    memset(dy,-1,sizeof(dy));
    for(int i=1; i<=n; i++){
        if(Mx[i]==-1){
            dx[i]=0; Q.push(i);
        }
    }
    while(!Q.empty()){
        int u=Q.front(); Q.pop();
        if(dx[u]>dis)   break;
        for(int i=0; i<V[u].size(); i++){
            int j=V[u][i];
            if(dy[j]!=-1)   continue;
            dy[j] = dx[u]+1;
            if(My[j]==-1)   dis=dy[j];
            else{
                dx[My[j]] = dy[j]+1;
                Q.push(My[j]);
            }
        }
    }
    return (dis!=inf);
}
bool dfs(int x){
    for(int i=0; i<V[x].size(); i++){
        int j= V[x][i];
        if(vis[j] || dy[j]!=dx[x]+1)  continue;
        vis[j]=1;
        if(My[j]!=-1 && dy[j]==dis) continue;
        if(My[j]==-1 || dfs(My[j])){
            My[j]=x; Mx[x]=j;
            return 1;
        }
    }
    return 0;
}
int MaxMatch(){
    int ans=0;
    memset(Mx,-1,sizeof(Mx));
    memset(My,-1,sizeof(My));
    while(search()){
        memset(vis,0,sizeof(vis));
        for(int i=1; i<=n; i++){
            if(Mx[i]==-1 && dfs(i)) ans++;
        }
    }
    return ans;
}
int main(){
    while(1){
        int a, b, x, y, z1, z2;
        IN(a); IN(b);
        if(!(a||b)) break;
        memset(f,0,sizeof(f));
        n=0;
        while(a--){
            IN(x); IN(y);
            z1 = x*101+y;
            z2 = z1+101;
            if(!f[z1]){
                f[z1]=++n;
                V[n].clear();
            }
            z1 = f[z1];
            if(!f[z2]){
                f[z2]=++n;
                V[n].clear();
            }
            z2 = f[z2];
            V[z1].pb(z2);
            V[z2].pb(z1);
        }
        while(b--){
            IN(x); IN(y);
            z1 = x*101+y;
            z2 = z1+1;
            if(!f[z1]){
                f[z1]=++n;
                V[n].clear();
            }
            z1 = f[z1];
            if(!f[z2]){
                f[z2]=++n;
                V[n].clear();
            }
            z2 = f[z2];
            V[z1].pb(z2);
            V[z2].pb(z1);
        }
        printf("%d\n", MaxMatch()>>1);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值