zoukankan      html  css  js  c++  java
  • 【BZOJ1433】[ZJOI2009] 假期的宿舍(二分图匹配入门)

    点此看题面

    大致题意:(n)个学生,其中一部分是在校学生,一部分不是,而在校学生中一部分回家,一部分不回家,并且我们用一个01矩阵表示学生之间相互认识关系。已知每个学生只能睡自己认识的人的床(当然,他也可以睡自己的床),问是否有一个方案使得所有学生都有床睡。

    建图

    这道题是一道图论题。对于这种图论题,我们首先要考虑的便是建图。

    不难想到,我们可以将每个人与其能睡的床连一条边,即:

    • 对于一个在校不回家的学生(i),我们将(i)与自己的床连一条边。
    • 对于一个在校且不回家不在校的学生(i),如果他认识一个在校的学生(j),我们将(i)(j)的床连一条边。

    之所以上面要强调不回家,是因为对于回家的学生,在给他连边是没有任何意义的。

    而这张图建成之后,应该不难发现它是一张二分图,那么原题就变成了一道求二分图最大匹配的题目,就可以用匈牙利算法来解决了。

    再看一眼数据范围,(n≤50),那么匈牙利算法的(O(n^2))复杂度在这道题目中不是轻松跑跑吗?

    代码

    #include<bits/stdc++.h>
    #define max(x,y) ((x)>(y)?(x):(y))
    #define min(x,y) ((x)<(y)?(x):(y))
    #define LL long long
    #define swap(x,y) (x^=y,y^=x,x^=y)
    #define tc() (A==B&&(B=(A=ff)+fread(ff,1,100000,stdin),A==B)?EOF:*A++)
    #define N 50
    #define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
    char ff[100000],*A=ff,*B=ff;
    using namespace std;
    int n,m,ee=0,lnk[N+5],vis[N+5],s[N+5],SchoolStudent[N+5],BackHome[N+5];
    struct edge
    {
        int to,nxt;
    }e[N*N+5]; 
    inline void read(int &x)
    {
        x=0;static char ch;
        while(!isdigit(ch=tc()));
        while(x=(x<<3)+(x<<1)+ch-48,isdigit(ch=tc()));
    }
    inline bool GetPoint(int x,int t)//为编号为x的点寻找一个匹配
    {
        register int i;
        for(i=lnk[x];i;i=e[i].nxt)//枚举每一个与x相邻的节点
        {
            if(!(vis[e[i].to]^t)) continue;//如果这个节点已经访问过了,就跳过
            vis[e[i].to]=t;//否则,标记这个节点已访问
            if(!s[e[i].to]||GetPoint(s[e[i].to],t))//如果这个节点没被匹配,或者与这个节点匹配的节点能找到一个新的节点匹配
            {
                s[e[i].to]=x;//标记这个节点与x匹配
                return true;//找到一个匹配,返回true
            }
        }
        return false;//说明找不到匹配,返回false
    } 
    int main()
    {
        register int i,j,T,x,ok;read(T);
        while(T--)
        {
            for(read(n),ee=0,ok=i=1;i<=n;++i) vis[i]=s[i]=lnk[i]=0;//多组数据,记得初始化
            for(i=1;i<=n;++i) read(SchoolStudent[i]);//读入每个学生是否是在校学生
            for(i=1;i<=n;++i) {read(BackHome[i]);if(!SchoolStudent[i]) BackHome[i]=0;}//读入每个学生是否回家,如果不是在校学生,默认其不回家
            for(i=1;i<=n;++i)
            {
                for(j=1;j<=n;++j)
                {
                    read(x);
                    if((x&&!BackHome[i]&&SchoolStudent[j])||(i==j&&SchoolStudent[i]&&!BackHome[i])) add(i,j);//如果i认识j,i不回家,且j是在校学生;或者i=j,i是在校学生,且i不回家,将i与j连一条边
                }
            }
            for(i=1;i<=n;++i) if(!BackHome[i]&&!GetPoint(i,i)) {ok=0;break;}//如果某个学生不回家,且找不到床,那么就说明没有使每个人都有床的方案
            puts(ok?"^_^":"T_T");
        }
        return 0;
    }
    
  • 相关阅读:
    Ural 1741 Communication Fiend(隐式图+虚拟节点最短路)
    OpenCV学习笔记——多种Smooth平滑处理
    HDU 1025 Constructing Roads In JGShining's Kingdom(二维LIS)
    POJ 2533 Longest Ordered Subsequence(LIS模版题)
    NBUT 1186 Get the Width(DFS求树的宽度,水题)
    Codeforeces 617E XOR and Favorite Number(莫队+小技巧)
    CodeBlocks的汉化、主题美化及其调试功能的实现
    PAT天梯赛练习题 L3-010. 是否完全二叉搜索树(完全二叉树的判断)
    POJ 2892 Tunnel Warfare(线段树单点更新区间合并)
    HDU 4031 Attack(线段树/树状数组区间更新单点查询+暴力)
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ1433.html
Copyright © 2011-2022 走看看