二分图

二分图的判定:

二分图: 让很多点分别放在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\n",ans);
}
int main(){
    scanf("%d\n",&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

 

简单例题: 一定仔细要看,特别是注释

P3355 骑士共存问题 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

#include <bits/stdc++.h> 
using namespace std;
#define ri register int
#define  M 1005

template <class G> void read(G &x)
{
    x=0;int f=0;char ch=getchar();
    while(ch<'0'||ch>'9'){x|=ch=='-';ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    x=f?-x:x;
    return ;
}

int n,m;

vector <int> p[M];
int pi[M],vis[M],flag[M];
bool dfs(int a)
{   

    for(ri i=0;i<p[a].size();i++)
    {
        int b=p[a][i];    //  看清楚是对哪一边的标记
        if(vis[b]) continue;     // 访问过的点一定要返回   
        vis[b]=1;
        if(!pi[b]||dfs(pi[b]))
        {
            pi[b]=a;
            return 1;
        }
    }
    return 0;
    
    
}
int main(){
    
    read(n);read(m);read(m);
    for(ri i=1;i<=m;i++)
    {
        int a,b;
        read(a);read(b);
        p[a].push_back(b);
        //p[b].push_back(a); // 注意这个不是那种无向图,边不要随便连接 
    }
    
    long long ans=0;
    for(ri i=1;i<=n;i++)
    {
        memset(vis,0,sizeof(vis));
        if(dfs(i)) ans++;
    }
    printf("%lld",ans);
    return 0;
    
}
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\n",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",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\n",ans);
    
    for(ri i=1;i<=n;i++)
    printf("%d ",pip[i]);
    
    return 0;
    
} 
View Code

 

posted @ 2019-11-07 18:28  VxiaohuanV  阅读(220)  评论(0编辑  收藏  举报