zoukankan      html  css  js  c++  java
  • 二分图

    二分图的判定:

    二分图: 让很多点分别放在2 端 ,每一个端内的点不能够相连接 , 2端的点可以任意连接。

    染色:

     小注意: 每一个点没有去过,就去,不要dfs(1) 就走了

    color[1]=1;
    bool dfs(int u)
    {
        for(ri i=head[u];i;i=bian[i].net)
        {
            int v=bian[i].to;
            if(color[v]&&color[v]!=color[u]) continue;
            if(color[v]&&color[v]==color[u]) return 0;
            if(!color[v])
            {
                color[v]=3-color[u];
                if(!dfs(v)) return 0;
            }
        }
        return 1;
    }
    View Code

    二分图的最大匹配:

    bool dfs(int u)
    {    
       
        for(int j=son[u];j;j=bian[j].net)
        {   
           int v=bian[j].val;
           if(vis[v]) continue ;
             vis[v]=1;
          if(pipei[v]==-1||dfs(pipei[v]))
          {
            pipei[u]=a;
            return true;
            }
        }
       return false;
    }
    
        for(i=1;i<=n;i++)
        {
            if(pipei[i]==-1)
            {   
                memset(vis,0,sizeof vis);
                ans=ans+dfs(i);
            }
        }
    View Code

    小定理: 出去最大匹配的点后,剩下的点一定不会相连接。

    相关定理: 转载:罗茜

    最小顶点覆盖:在二分图中寻找一个尽量小的点集,使图中每一条边至少有一个点在该点集中。

      最小顶点覆盖 == 最大匹配。

      反证法证明:假设当前存在一条两个端点都不在最小顶点覆盖点集中,那么这么光芒四射的边定可以增大最大匹配边集,与最大匹配矛盾,所以得证。

     

    最小路径覆盖:在二分图中寻找一个尽量小的边集,使图中每一个点都是该边集中某条边的端点。

      最小路径覆盖 == 顶点数 - 最大匹配。

      证明:因为一条边最多可以包含两个顶点,所以我们选边的时候让这样的边尽量多,也就是说最大匹配的边集数目咯。剩下的点就只能一个边连上一个点到集合里啦。

     

    最大独立集:在N个点中选出来一个最大点集,使这个点集中的任意两点之间都没有边。

      最大独立集 == 顶点数 - 最大匹配。

      证明:因为去掉最大匹配两端的顶点去掉以后,剩下的点肯定是独立集。我们再从每个匹配里面挑选出来一个点加入到独立集中,也是不会破坏原有独立集的独立性的。

    一些题目的联系:
       题目: 下面2道题 建边很关键

    问题 D: 【高级算法】火力网
    时间限制: 1 Sec  内存限制: 128 MB
    提交: 13  解决: 11
    [提交] [状态] [讨论版] [命题人:外部导入]
    题目描述
    给出一个N*N的网格,用'.'表示空地,用'X'表示墙。在网格上放碉堡,可以控制所在的行和列,但不能穿过墙。
    问:最多能放多少个碉堡?
    例如,下图最多可放5个碉堡(黑色圆点)
    
    输入
    第1行:一个整数N(N<=20)
    接下来N行,每行N个字符
    输出
    第1行:1个整数,表示最多可放碉堡数。
    样例输入 Copy
    4
    .X..
    ....
    XX..
    ....
    样例输出    Copy
    5
    View Code

        代码:

    #include <bits/stdc++.h>
    using namespace std;
    const int M = 10005;
    const int N = 10005;
    #define ri register int
    bool mp[1010][1010];
    int n,m,f[1010][1010];
    int vis[N],pipei[N];
    vector <int> q[10010];
     
    void init()
    {
        int cent=1;
        for(ri j=1;j<=n;j++)
        {
           for(ri i=1;i<=n;i++)
           {
            if(!mp[i][j]) f[i][j]=cent;
            else if(!mp[i-1][j]) cent++;
            }
            cent++; 
        }
        m=cent,cent++;
        for(ri i=1;i<=n;i++)
        {
            for(ri j=1;j<=n;j++)
            {
                if(!mp[i][j]) q[f[i][j]].push_back(cent);
                else if(!mp[i][j-1]) cent++;
            }
            cent++;
        }
    }
    bool dfs1(int u)
    {
        for(ri i=0;i<q[u].size();i++)
        {
            int v=q[u][i];
            if(vis[v]) continue;
              vis[v]=1;
            if(!pipei[v]||dfs1(pipei[v]))
            {
                pipei[v]=u;
                return 1;
            }
        }
        return 0;
    }
    void getans()
    {
        int ans=0;
        for(ri i=1;i<=m;i++)
        {
            memset(vis,0,sizeof vis);
            if(!pipei[i])
            ans+=dfs1(i);
        }
        printf("%d
    ",ans);
    }
    int main(){
        scanf("%d
    ",&n);
        for(ri i=1;i<=n;i++)
        for(ri j=1;j<=n;j++)
        {
            char c;
            cin>>c;
            if(c=='X')
            mp[i][j]=1;
        }
        init();
        getans();
    }
    View Code

     题目:

    问题 C: [Balic2001]棋盘上的骑士
    时间限制: 2 Sec  内存限制: 64 MB
    提交: 8  解决: 5
    [提交] [状态] [讨论版] [命题人:外部导入]
    题目描述
    一个N*N的棋盘上,有一些小方格被拿走了,不能放置骑士,其它位置可以放。现要在棋盘上放若干骑士,要求任一个骑士都不能在其他骑士的攻击点上。请算出棋盘上最多能有几个骑士。骑士攻击范围如图所示(S是骑士的位置,X表示马的攻击点)  
    输入
    第一行包含2个整数n和m,用单个的空格分开,1<=n<=200 , 0<=m < 40000;n 是国际象棋棋盘的大小,m是被拿走的格子数。 下面m行每行包含 2 个整数:x和y,用单个的空格分开,1<=x,y<=n,这些是被拿走的格子的坐标。 棋盘的左上角的坐标是(1,1),右下角是(n,n)。拿走的格子没有重复的。
    输出
    一个整数,它应该是能放在国际象棋棋盘上的互不攻击对方的马的最大的数量。
    样例输入 Copy
    3 2
    1 1
    3 3
    样例输出    Copy
    5
    View Code

     代码:

    #include <bits/stdc++.h>
    using namespace std;
    const int N  = 10005;
    const int M  = 20002;
    #define ri register int
    int m,n;
    bool mp[1010][1010];
    int g[1010][1010],xq[M],yq[M],link[M],vis[M];
    int X[20]={2,1,-1,-2,-2,-1,1,2};
    int Y[20]={1,2,2,1,-1,-2,-2,-1};
    bool check(int x,int y)
    {
        if(x<1||y<1||y>n||x>n||mp[x][y])
         return 0;
         return 1;
    }
    bool dfs(int u)
    {
        int x=xq[u];
        int y=yq[u];
        int jishu1=7;
        while(jishu1>=0)
        {
          int xx=x+X[jishu1];
          int yy=y+Y[jishu1--];
          if(!check(xx,yy)) continue;
          int trmp=g[xx][yy];
           if(vis[trmp]) continue;
           vis[trmp]=1;
           if(!link[trmp]||dfs(link[trmp]))
           {
             link[trmp]=u;
             return 1;
           }
        }
        return 0;
    }
    int main(){
        scanf("%d%d",&n,&m);
        for(ri i=1;i<=m;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            mp[a][b]=1;
         }
         int one=0,two=0;
        for(ri i=1;i<=n;i++)
        for(ri j=1;j<=n;j++)
        {
            if(mp[i][j]) continue;
            if(!((i+j)&1))
            {
                g[i][j]=++two;
            }
            else
            {
                xq[++one]=i;
                yq[one]=j;
            }
        }
        int ans=n*n-m;
        for(ri i=1;i<=one;i++)
        {
            memset(vis,0,sizeof vis);
            if(dfs(i)) ans--;
        }
        printf("%d
    ",ans);
    }
    View Code

     题目:二分图+floyed(为什么不能加匹配呢)

    1995: 【NOIP模拟赛】捉迷藏
    时间限制: 1 Sec  内存限制: 128 MB
    提交: 9  解决: 3
    [提交] [状态] [讨论版] [命题人:外部导入]
    题目描述
    vani和cl2在一片树林里捉迷藏…… 
    这片树林里有N座房子,M条有向道路,组成了一张有向无环图。
    树林里的树非常茂密,足以遮挡视线,但是沿着道路望去,却是视野开阔。如果从房子A沿着路走下去能够到达B,那么在A和B里的人是能够相互望见的。
    现在cl2要在这N座房子里选择K座作为藏身点,同时vani也专挑cl2作为藏身点的房子进去寻找,为了避免被vani看见,cl2要求这K个藏身点的任意两个之间都没有路径相连。
    为了让vani更难找到自己,cl2想知道最多能选出多少个藏身点? 
    输入
    第一行两个整数N,M。
    接下来M行每行两个整数x、y,表示一条从x到y的有向道路。 
    输出
    一个整数K,表示最多能选取的藏身点个数。 
    样例输入 Copy
    4 4
    1 2
    3 2
    3 4
    4 2
    样例输出    Copy
    2
    提示
    对于20% 的数据,N≤10,M<=20。
    对于60% 的数据, N≤100,M<=1000。
    对于100% 的数据,N≤200,M<=300001<=x,y<=N。 
    View Code

     代码:

    #include <bits/stdc++.h>
    using namespace std;
    const int M = 30050;
    const int N = 10005;
    #define ri register int
    int n,m,vis[N],pipei[N];
    bool mp[1010][1010];
    bool dfs1(int u)
    {
        for(ri i=1;i<=n;i++)
        {
        if(!vis[i]&&mp[u][i])
        {
            vis[i]=1;
            if(!pipei[i]||dfs1(pipei[i]))
            {
                pipei[i]=u;
                return 1;
            }
        }
        }
        return 0;
    }
    int main(){
        scanf("%d%d",&n,&m);
        for(ri i=1;i<=m;i++)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            mp[a][b]=1;
        }
        for(ri k=1;k<=n;k++)
        for(ri i=1;i<=n;i++)
        for(ri j=1;j<=n;j++)
        {
            if(k==i||i==j||j==k) continue;
            mp[i][j]=mp[i][j]||(mp[i][k]&&mp[k][j]);
        }
        int ans=0;
        for(ri i=1;i<=n;i++)
        {
            memset(vis,0,sizeof vis);
             
            if(dfs1(i)) ans++;
        }
        printf("%d
    ",n-ans);
        return 0;
    }
    View Code

    二分图的带权的最大匹配 KM 算法 时间复杂度 n^4   

    pip 要初始化为-1;

    #include <bits/stdc++.h>
    using namespace std;
    #define ri register int 
    #define M 505
    
    
    int n,m;
    struct edge{
        int d,val;
        edge(){}
        edge (int a,int b){d=a,val=b;}
    };
    vector <edge> p[M];
    
    int vx[M],vy[M];
    int vist[M];
    int pip[M];
    
    int xx;
    
    stack <int> q1;
    stack <int> q2;
    
    bool dfs(int a)
    {
        for(ri i=0;i<p[a].size();i++)
        {
            int b=p[a][i].d;
            if(vist[b]) continue;
            vist[b]=1;
            int t=vx[a]+vy[b];
        
            if(t==p[a][i].val)       // 这个顺序很重要 
            {
                if(pip[b]==-1||dfs(pip[b]))
                {
                    pip[b]=a;
                    q1.push(a);
                    q2.push(b);
                    
                    return true;
                }
            }else
            xx=min(vx[a]-vy[b]-p[a][i].val,xx);
            
        }
        return false;
        
    }
    
    int main(){
        
        memset(pip,-1,sizeof(pip)); //// remember  
        scanf("%d%d",&n,&m);
        for(ri i=1;i<=m;i++)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            edge d;d=edge(b,c);
            p[a].push_back(d);
        }
        for(ri i=1;i<=n;i++)
        {
            int trmp = -19980739;
            
            for(ri j=0;j<p[i].size();j++)
            {
                trmp=max(trmp,p[i][j].val);
            }
            vx[i]=trmp;
        }
        
        for(ri i=1;i<=n;i++)
        {
            memset(vist,0,sizeof(vist));
            xx=19980739;
            
            while(!dfs(i))
            {
                memset(vist,0,sizeof(vist));
                while(!q1.empty())
                {
                    int a=q1.top();
                    q1.pop();
                    vx[a]-=xx;
                }
                while(!q2.empty())
                {
                    int a=q2.top();
                    q2.pop();
                    vy[a]+=xx;
                }
                xx=19980739;
            }
        }
        long long ans=0;
        
        for(ri i=1;i<=n;i++)
        {
            ans+=vx[i];
            ans+=vy[i];
        }
        printf("%lld
    ",ans);
        
        for(ri i=1;i<=n;i++)
        printf("%d ",pip[i]);
        
        return 0;
        
    } 
    View Code
  • 相关阅读:
    优化SQL查询:如何写出高性能SQL语句
    提高SQL执行效率的16种方法
    Spring Ioc DI 原理
    java内存泄漏
    转:js闭包
    LeetCode Best Time to Buy and Sell Stock III
    LeetCode Best Time to Buy and Sell Stock with Cooldown
    LeetCode Length of Longest Fibonacci Subsequence
    LeetCode Divisor Game
    LeetCode Sum of Even Numbers After Queries
  • 原文地址:https://www.cnblogs.com/Lamboofhome/p/11814122.html
Copyright © 2011-2022 走看看