zoukankan      html  css  js  c++  java
  • 二分图的基础与基本应用:POJ 1469&&POJ 3041&&HDU 2255&&HDU1533

    先把最基础的概念搞清楚:

    二分图:有两组顶点,一组顶点记为 S1 ,另一组记为 S2  S1  S2 没有公共的元素,并且所有的边都是连接 S1  S2 中的点的,对于 S1  S2 本身,它们内部的任何两个点都没有边相连,这样的无向图就叫二分图。

    点覆盖集:即是一个点集,使得所有边至少有一个端点在集合里。

    边覆盖集:即是一个边集,使得所有点都与集合里的边邻接。(其实我觉得这句话很难理解)

    但是结合这句呢:在一个P*P的有向图中,最小路径覆盖=|P|-最大匹配数。(因为有匹配数的存在,本来要P个边的覆盖因此减小了)

    二分图的最小顶点覆盖数等于最大匹配数。

    匈牙利算法:主要二分图的最大匹配数。其实我觉得自学这东西不适合我,可能以前而到现在仍受体制化的影响,有老师真好(有依赖感了)。。并且我觉得有些自己理解花的时间比别人讲的时间要多很多很多!!!

    等等。。。

    除了点覆盖数还有边覆盖数。。。

    下面M是G的一个匹配。
    M-交错路:p是G的一条通路,如果p中的边为属于M中的边不属于M但属于G中的边交替出现,则称p是一条M-交错路
    M-饱和点:对于v∈V(G),如果v与M中的某条边关联,则称v是M-饱和点,否则称v是非M-饱和点。
    M-可增广路:p是一条M-交错路,如果p的起点和终点都是非M-饱和点,则称p为M-可增广路。(不要和流网络中的增广路径弄混了)

     匈牙利算法:(用于求最大匹配数)

    COURSES POJ 1469

    一道匈牙利算法的模板题:

     1 #include<iostream>
     2 #include<algorithm>
     3 #include<stdio.h>
     4 #include<string.h>
     5 using namespace std;
     6 int a[110][310];//邻接矩阵。用来存储边的信息,连接或没连接
     7 int vis[310],b1[310],m;//一个用来搜索的标记,一个记录点匹配点的编号
     8 int dfs(int x)
     9 {
    10     int i;
    11     for(i=1;i<=m;i++)
    12         if(a[x][i]&&!vis[i])//连通了并且没被标记
    13         {
    14             vis[i]=1;//进行标记
    15             if(b1[i]==0||dfs(b1[i]))//没有被匹配或者前面的有增广路
    16             {
    17                 b1[i]=x;//进行记录
    18                 return 1;//返回能匹配
    19             }
    20         }
    21     return 0;//返回没有被匹配
    22 }
    23 int main()
    24 {
    25     int t,n,i,j,a2,a1,s;
    26     scanf("%d",&t);
    27     while(t--)
    28     {
    29         memset(a,0,sizeof(a));
    30         scanf("%d%d",&n,&m);
    31         for(i=1;i<=n;i++)
    32         {
    33             scanf("%d",&a1);
    34             while(a1--)
    35             {
    36                 scanf("%d",&a2);
    37                 a[i][a2]=1;
    38             }
    39         }
    40         s=0;
    41         memset(b1,0,sizeof(b1));
    42         for(i=1;i<=n;i++)
    43         {
    44             memset(vis,0,sizeof(vis));//每次都要更新
    45             if(dfs(i))
    46             s++;
    47         }
    48         if(s==n)
    49             printf("YES
    ");
    50         else
    51             printf("NO
    ");
    52     }
    53     return 0;
    54 }

    再就是求最小顶点覆盖数:(因为理论上就等于最大匹配数)

    Asteroids  POJ 3041

    直接上匈牙利算法求最大匹配数就行了。。。

     1 #include<iostream>
     2 #include<algorithm>
     3 #include<stdio.h>
     4 #include<string.h>
     5 using namespace std;
     6 int a[505][505],vis[505],b1[505],n;
     7 int dfs(int x)
     8 {
     9     int i;
    10     for(i=1;i<=n;i++)
    11     {
    12         if(a[x][i]&&!vis[i])
    13         {
    14             vis[i]=1;
    15             if(b1[i]==0||dfs(b1[i]))
    16             {
    17                 b1[i]=x;
    18                 return 1;
    19             }
    20         }
    21     }
    22     return 0;
    23 }
    24 int main()
    25 {
    26     int m,a1,a2,s,i;
    27     while(scanf("%d%d",&n,&m)!=EOF)
    28     {
    29         memset(a,0,sizeof(a));
    30         while(m--)
    31         {
    32             scanf("%d%d",&a1,&a2);
    33             a[a1][a2]=1;
    34         }
    35         memset(b1,0,sizeof(b1));
    36         s=0;
    37         for(i=1;i<=n;i++)
    38         {
    39             memset(vis,0,sizeof(vis));
    40             if(dfs(i))
    41                 s++;
    42         }
    43         printf("%d
    ",s);
    44     }
    45     return 0;
    46 }

    之后就是求最大权匹配:KM算法。。。

    本人觉得有好多细节部分需要多次编写与记忆!

    突出的一点要用顶标来计算,于是乎就多了两个数组,之后再交错树那里又多了一个数组!

    然而判断方面因为有两顶标,就又多了一个数组。。。算算要开7个数组吧!!!(其中有两对)

    奔小康赚大钱 HDU 2255

    思路具体参见:http://www.cnblogs.com/jackge/archive/2013/05/03/3057028.html

     1 #include<iostream>
     2 #include<stdio.h>
     3 #include<algorithm>
     4 #include<string.h>
     5 using namespace std;
     6 int vix[310],viy[310],link[310],a[310][310],lx[310],ly[310],nx,ny,slack[310];
     7 int inf=1000005;
     8 int dfs(int x)//跟匈牙利算法差不多
     9 {
    10     int i,temp;
    11     vix[x]=1;//不同点
    12     for(i=1;i<=ny;i++)
    13     {
    14         if(viy[i])
    15             continue;
    16         temp=lx[x]+ly[i]-a[x][i];//找的是相等子图
    17         if(temp==0)
    18         {
    19             viy[i]=1;
    20             if(link[i]==-1||dfs(link[i]))
    21             {
    22                 link[i]=x;
    23                 return 1;
    24             }
    25         }
    26         else if(slack[i]>temp)
    27             slack[i]=temp;
    28     }
    29     return 0;
    30 }
    31 int main()
    32 {
    33     int i,j,s,n;
    34     while(scanf("%d",&n)!=EOF)
    35     {
    36         nx=ny=n;
    37         for(i=1;i<=n;i++)
    38             for(j=1;j<=n;j++)
    39                 scanf("%d",&a[i][j]);
    40         memset(lx,0,sizeof(lx));
    41         memset(ly,0,sizeof(ly));
    42         memset(link,-1,sizeof(link));
    43         for(i=1;i<=nx;i++)
    44             for(j=1;j<=ny;j++)
    45                 if(a[i][j]>lx[i])
    46                     lx[i]=a[i][j];
    47         for(i=1;i<=nx;i++)
    48         {
    49             for(j=1;j<=nx;j++)
    50                 slack[j]=inf;
    51             while(1)
    52             {
    53                 memset(vix,0,sizeof(vix));//记录搜索的每次都更新
    54                 memset(viy,0,sizeof(viy));//如上
    55                 if(dfs(i))//有匹配
    56                     break;
    57                 int d=inf;//下面是核心:凑成一个匹配出来
    58                 for(j=1;j<=ny;j++)
    59                     if(!viy[j]&&d>slack[j])
    60                         d=slack[j];
    61                 for(j=1;j<=nx;j++)
    62                     if(vix[j])
    63                         lx[j]-=d;
    64                 for(j=1;j<=ny;j++)
    65                     if(viy[j])
    66                         ly[j]+=d;
    67                     else
    68                         slack[j]-=d;
    69             }
    70         }
    71         s=0;
    72         for(i=1;i<=ny;i++)
    73             if(link[i]!=-1)
    74             s+=a[link[i]][i];
    75         printf("%d
    ",s);
    76     }
    77     return 0;
    78 }

     经过渊神的细心指导,总算弄懂清楚不少了!在此十分感谢!!!

    根据KM算法,找不到匹配时,就要用交错树来凑成一个!当然希望每进行一次,能凑成一个。。。于是就有slack[]数组了

    vix[]与viy[]是判断是否在交错树中的。。。

    根据算法:在树中的lx[]减d,ly[]加d。。。(当然这个值d得在交错树外面找啊,因为进入交错树的都是有匹配的。。。)

    最关键的:因为呢,slack[j] = lx[i] + ly[j] - w[i][j],而lx[]减小,其他的不变,(ly[]是交错树外面的),那么slack[j]也要减小d了。。。

    时刻谨记着二分图是以左边为基点,向右边进行查找的(一旦左边的向下移动了,必定已经找到了。。。)

     之后我发现求最大权匹配和最小权匹配是多么的不同啊!!

    在建图的时候,(将每条边的权值变为负数。然后lx[i]初始化为-INT_MAX,结果输出-ans)!!,就可以得到最小权值。

     这句话的重点在:要非常大啊!!

    Going Home HDU1533

    废话也不说了,先化成二分图的形式。。。之后慢慢比对上面的代码

      1 #include<iostream>
      2 #include<algorithm>
      3 #include<stdio.h>
      4 #include<math.h>
      5 #include<string.h>
      6 using namespace std;
      7 int vix[105],viy[105],link[105],b[105][105],lx[105],ly[105],d,slack[105];
      8 int inf=100000005;//要非常大,少个零就超时!!
      9 struct line
     10 {
     11     int x;
     12     int y;
     13 }a1[105],b1[105];
     14 int dfs(int x)//跟匈牙利算法差不多
     15 {
     16     int i,temp;
     17     vix[x]=1;//不同点
     18     for(i=1;i<=d;i++)
     19     {
     20         if(viy[i])
     21             continue;
     22         temp=lx[x]+ly[i]-b[x][i];//找的是相等子图
     23         if(temp==0)
     24         {
     25             viy[i]=1;
     26             if(link[i]==-1||dfs(link[i]))
     27             {
     28                 link[i]=x;
     29                 return 1;
     30             }
     31         }
     32         else if(slack[i]>temp)
     33             slack[i]=temp;
     34     }
     35     return 0;
     36 }
     37 int main()
     38 {
     39     char a[105][105];
     40     int i,n,m,d1,j,s;
     41     while(scanf("%d%d",&n,&m)!=EOF)
     42     {
     43         if(m==0&&n==0)
     44             break;
     45         for(i=0;i<n;i++)
     46             scanf("%s",a[i]);
     47         d=0;d1=0;
     48         for(i=0;i<n;i++)
     49             for(j=0;j<m;j++)
     50         {
     51             if(a[i][j]=='m')
     52             {
     53                 d++;
     54                 a1[d].x=i;
     55                 a1[d].y=j;
     56             }
     57             if(a[i][j]=='H')
     58             {
     59                 d1++;
     60                 b1[d1].x=i;
     61                 b1[d1].y=j;
     62             }
     63         }
     64         for(i=1;i<=d;i++)
     65             for(j=1;j<=d;j++)
     66             b[i][j]=-abs(a1[i].x-b1[j].x)-abs(a1[i].y-b1[j].y);//全变成负值
     67         memset(lx,-inf,sizeof(lx));//这里初始为负的,因为下面是负值,你要求较大值。。不然全为零了
     68         memset(ly,0,sizeof(ly));
     69         memset(link,-1,sizeof(link));
     70         for(i=1;i<=d;i++)
     71             for(j=1;j<=d;j++)
     72                 if(b[i][j]>lx[i])//赋负值的原因
     73                     lx[i]=b[i][j];
     74         for(i=1;i<=d;i++)
     75         {
     76             for(j=1;j<=d;j++)
     77                 slack[j]=inf;
     78             while(1)
     79             {
     80                 memset(vix,0,sizeof(vix));//记录搜索的每次都更新
     81                 memset(viy,0,sizeof(viy));//如上
     82                 if(dfs(i))//有匹配
     83                     break;
     84                 int h=inf;//下面是核心:凑成一个匹配出来
     85                 for(j=1;j<=d;j++)
     86                     if(!viy[j]&&h>slack[j])
     87                         h=slack[j];
     88                 for(j=1;j<=d;j++)
     89                 {
     90                     if(vix[j])
     91                         lx[j]-=h;
     92                     if(viy[j])
     93                         ly[j]+=h;
     94                     else
     95                         slack[j]-=h;
     96                 }
     97             }
     98         }
     99         s=0;
    100         for(i=1;i<=d;i++)
    101             s+=b[link[i]][i];
    102         printf("%d
    ",-s);
    103 
    104     }
    105     return 0;
    106 }
  • 相关阅读:
    tomcat 服务器的几个重要监听 方法 与 使用
    webSocket 前端 js 加入 心跳机制 的基本写法
    Palinwords (处理回文串)
    Anti-Rhyme Pairs (求最长公共前缀)
    Extend to Palindrome (顺序Hash和逆序Hash处理回文)
    Milk Patterns (hash + 二分)
    次小生成树
    Borg Maze (BFS预处理+最小生成树)
    P1126 机器人搬重物
    P1141 01迷宫 (记忆化搜索)
  • 原文地址:https://www.cnblogs.com/tt123/p/3265533.html
Copyright © 2011-2022 走看看