最大团&&二分图2

巴塞罗那又输球了,在我眼里或许它是完美的,只能希望煤老板早日康复了吧。

最大团:子图(包括自己)中最大的完全图。

补图:将图补成完全图,缺少的边以及所有的点组成的图。

最小覆盖集:对于每条边至少一个点被选中。

最大独立集就是其补图的最大团。

二分图的最大独立集:集合中任意两个点都没有边相连。

最小路径覆盖: DAG的最小路径覆盖是指找最小数目的互相不相交的有向路径,满足DAG的所有顶点都被覆盖。

通俗的讲就是从起点开始,只经过每个点一次,最后回到这个点。

http://www.cnblogs.com/pushing-my-way/archive/2012/08/08/2627993.html

此处讲解的十分详细。

代码还是要自己注释一下的。

要注意这求的是有环无向图的最大团。

#include <iostream>
  #include <memory.h>
  #include <stdio.h>
  using namespace std;
  
  const int maxnum=101;
  bool array[maxnum][maxnum];
  bool use[maxnum]; //进入团的标号
  int cn,bestn,p,e;
 
 void dfs(int i)
 {
     int j;
     bool flag;
 
     if(i>p)
     {
         bestn=cn;
         printf("%d\n",bestn);
         for(j=1;j<=p;j++)
             if(use[j])
                 printf("%d ",j);
         printf("\n");
         return ;
     }
 
     flag=true;
     for(j=1;j<i;j++)
         if(use[j]&&!array[j][i])
         {
             flag=false;
             break;
         }                         //判断与相邻的点是否有连接
     if(flag)
     {
         cn++;
         use[i]=true;
         dfs(i+1);
         cn--;
     }
     if(cn+p-i>bestn)  //剩下未搜索的点加团中的点少于现在的答案
     {
         use[i]=false;
         dfs(i+1);
     }
 }
 
 int main()
 {
     int num,i,u,v;
     scanf("%d",&num);
     while(num--)
     {
         memset(array,false,sizeof(array));
         memset(use,false,sizeof(use));
         scanf("%d%d",&p,&e);
         for(i=0;i<e;i++)
         {
             scanf("%d%d",&u,&v);
             array[u][v]=true;
             array[v][u]=true;
         }
 
         cn=bestn=0;
         dfs(1);
         //printf("%d\n",bestn);
     }
 
     return 0;
}

这题要注意的是有可能有环存在,所以不可以用二分图的最大独立集=点数-最大匹配数来解决。(至于怎么解决后文会有提到)

1.1求二分图的最小覆盖集与最大独立集。

公式:最大独立集=点数-最大匹配数

最大匹配数=最小覆盖集

LA3415

题意:男女两种人,选中的男女要求满足四种条件。男女人数最大多少。

解法:将不满足条件的男女之间连一条线,最后就变成了求二分图的最大独立集。

代码由于与求最大匹配几乎相同,除了建图有一些不同以外,就不贴了。

1.2求具体最小覆盖集的点

标记右边未匹配边的顶点,并从右边未匹配边的顶点出发,按照边:未匹配->匹配->未匹配...,的原则标记途中经过的顶点,则最后一条经过的边必定为匹配边。重复上述过程,直到右边不再含有未匹配边的点。

记得到的左边已标记的点和右边未标记的点为S, S即为所求的最小顶点集。

uva11419

直接上代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int MAXN = 1111;

bool w[MAXN][MAXN],vis_x[MAXN],vis_y[MAXN];
int s[MAXN],t[MAXN],left[MAXN];

int match(int i,int c)
{
    s[i] = 1;
    for(int j = 1;j <= c;j++)
        if(w[i][j] && !t[j])
        {
            t[j] = 1;
            if(!left[j] || match(left[j],c))
            {
                left[j] = i;
                return 1;
            }
        }
    return 0;
}

void solve(int r,int c)
{
    memset(left,0,sizeof(left));
    memset(vis_x,0,sizeof(vis_x));
    memset(vis_y,0,sizeof(vis_y));
    int ans = 0;
    for(int i = 1;i <= r;i++)
        for(int j = 1;j <= c;j++)
            if(w[i][j] && !vis_x[i] && !vis_y[j])
            {
                vis_x[i] = vis_y[j] = 1;
                left[j] = i;
                ans++;
            }
    for(int i = 1;i <= r;i++)
    {
        memset(s,0,sizeof(s));
        memset(t,0,sizeof(t));
        if(!vis_x[i] && match(i,c))
            ans++;
    }
    printf("%d",ans);
}

int right[MAXN];

void print(int r,int c)
{
    memset(s,0,sizeof(s));
    memset(t,0,sizeof(t));
    memset(right,0,sizeof(right));
    for(int i = 1;i <= c;i++)
        if(left[i])
        {
            right[left[i]] = 1;
        }
    for(int i = 1;i <= r;i++)
        if(!right[i])
        {
            match(i,c);
        }
    for(int i = 1;i <= r;i++)
        if(!s[i]) printf(" r%d",i);
    for(int i = 1;i <= c;i++)
        if(t[i]) printf(" c%d",i);
    puts("");
}

int main()
{
    int r,c,n;
    while(~scanf("%d%d%d",&r,&c,&n) && r)
    {
        memset(w,0,sizeof(w));
        while(n--)
        {
            int a,b;
            scanf("%d%d",&a,&b);
            w[a][b] = 1;
        }
        solve(r,c);
        print(r,c);
    }
    return 0;
}
2.1 DAG最短路径覆盖

 最小路径覆盖=N-最大匹配

LA 3126

题意:有 n 个顾客 , 需要坐出租车从一个地方去另一个地方 , 每个顾客的出发时间、出发地点、目的地点都已给出 , 从出发地点到目的地点的时间为两地之间的路径长度 , 并且出租车要比顾客的出发时间早一分钟到达 , 问最少需要派出多少辆出租车。

解法:我们先这样来构图 , 每个顾客是一个结点,如果同一个出租车在接完客人 u 之后还来得及节客人 v , 那么就在 u 到 v 之间连一条有向边 。 由此可以发现 , 这个图是一个DAG , 那么我们就只需要找最小路径覆盖(最小路径覆盖:是指在图中找尽量少的路径,去覆盖每一个点,并且每个路径之间没有公共点)。

对于最小路径覆盖,我们先进行拆点  , 把图边为一个二分图 , 把所有结点 拆为两个结点 , 如 结点 u 拆为 u1 , u2 , 如果 u 到 v 有一条有向边 , 那么我们就连接 u1 到 v2 的一条边。

这时我们只需要求出这个图的最大匹配 , 结果就是: n - m(最大匹配数)。

为什么?

因为匹配和路径覆盖是一一对应的 , 对于路径覆盖中的每条简单路径 , 除了最后一个“结尾结点”之外,都有唯一的后继与其对应(也就是匹配结点) , 因此匹配数就是非“结尾结点”的个数 , 但匹配数达到最大时 , 也就是“结尾结点”最小 , 也就是路径最小。

代码:

#include <iostream>  
#include <stdio.h>  
#include <string.h>  
#include <vector>  
#include <cmath>  
  
using namespace std;  
  
#define maxn 520  
  
vector<int>grap[maxn];  
int cab[maxn][4] , cab_t[maxn][2];  
int n;  
int pre[maxn] , cx[maxn] , cy[maxn];  
  
void init()  
{  
    for(int i = 0; i < n; i++)  
        grap[i].clear();  
    memset(cx , -1 , sizeof(cx));  
    memset(cy , -1 , sizeof(cy));  
}  
  
void create_grap()  
{  
    int i , j;  
    int x , y;  
    int h , m;  
    for(i = 0; i < n; i++)  
    {  
        y = abs(cab[i][0]-cab[i][2])+abs(cab[i][1]-cab[i][3]);  
        for(j = 0; j < n; j++)  
        {  
            if(i == j)  continue;  
            x = y+abs(cab[j][0]-cab[i][2])+abs(cab[j][1]-cab[i][3]);  
            m = (x+cab_t[i][1])%60;  
            h = cab_t[i][0]+(x+cab_t[i][1])/60;  
            if(h < cab_t[j][0] || (h == cab_t[j][0]&&m < cab_t[j][1]))  
                grap[i].push_back(j);  
        }  
    }  
}  
  
int findpath(int u)  
{  
    int i , v;  
    for(i = 0; i < grap[u].size(); i++)  
    {  
        v = grap[u][i];  
        if(!pre[v])  
        {  
            pre[v] = 1;  
            if(cy[v] == -1 || findpath(cy[v]))  
            {  
                cy[v] = u;  
                cx[u] = v;  
                return 1;  
            }  
        }  
    }  
    return 0;  
}  
  
int match()  
{  
    int i , cas = 0;  
    for(i = 0; i < n; i++)  
        if(cx[i] == -1)  
        {  
            memset(pre , 0 , sizeof(pre));  
            cas += findpath(i);  
        }  
  
    return cas;  
}  
  
int main()  
{  
    int t;  
    scanf("%d" , &t);  
    while(t--)  
    {  
        scanf("%d" , &n);  
        init();  
  
        int i ;  
        for(i = 0; i < n; i++)  
            scanf("%d:%d %d %d %d %d" , &cab_t[i][0] , &cab_t[i][1] , &cab[i][0] , &cab[i][1] , &cab[i][2] , &cab[i][3]);  
        create_grap();  
  
        int cas = match();  
        cout<<n-cas<<endl;  
    }  
    return 0;  
}  
估计二分图还要来一波专题,未完待续……

















你可能感兴趣的:(最大团&&二分图2)