zoukankan      html  css  js  c++  java
  • UVA 11419 SAM I AM(最大二分匹配&最小点覆盖:König定理)

    题意:在方格图上打小怪,每次可以清除一整行或一整列的小怪,问最少的步数是多少,又应该在哪些位置操作(对输出顺序没有要求)。

    分析:最小覆盖问题

      这是一种在方格图上建立的模型:令S集表示“行”,T集表示“列”,那么小怪站的位置w(i,j),就是二分图上的边。如此建图,那么每次清除,就是把与某个点相连的边全部清除,问最少选择多少个点。(这也是最小点覆盖的概念:选择尽量少的点,使得每条边至少有一个端点被选中)

      这里有一个König定理:最大二分匹配数==最小覆盖点数。

      既然是求最小点覆盖,那么自然是选那些所连边数多的点,不过貌似不好安排啊?

      先从简单问题开始讨论:找到必然要选的点。对于一棵树,为了覆盖到叶子节点所在的边,必须选择该条边上的两个端点之一。不用问,按照最小覆盖的原则,必然是要选非叶子节点了。把所有与选择的点相连的边都处理掉,就相当于是这棵树的最下面一层被砍掉了(叶子的爷爷成为了新的叶子)。那么对于剩下的树,仍然可以这样操作,最终得到了最小点覆盖。幸运的是,树属于二分图,而二分图虽然不一定是树(存在偶环),但可以构建出匈牙利树(交错树),是可以借鉴的。

      聪明的地球人提出了如下算法:

      对于一个二分图,求出其最大匹配。然后取左侧(S侧)所有的未匹配点,按照增广路算法,寻找交错路径(路径上的点都是匹配点),标记掉路径上的所有点(不存在重复标记)。那么左侧(S侧)所有的未标记点,与右侧(T侧)所有的标记点,就是实现最小点覆盖的点。

      1、为什么这些点可以覆盖所有边?

      如果还有没有覆盖的边,那么一定是一些左端点已标记,右端点未标记的边。而通过我们算法中的构造,不存在这样的边。为什么呢?回忆一下增广路算法,我们的起点选择的是S侧的未匹配点,之后选择一条边走到T侧(该点必然是匹配点);因为要走交错路径,就沿着匹配边回到了S侧...可以想象成每次只是从S侧走到T侧,然后沿匹配边回到S侧。所以不存在S端已标记,T端未标记的线段。所以所有边都被覆盖了。

      2、为什么这些点的在数量上等于最大二分匹配的边数?

      因为每条匹配边上恰好选择一个点。为什么是这样呢?注意,最小点覆盖的点集包括:S侧的未标记点,和T侧的标记点。有增广路算法可知(同上一个问题相似),若T侧的点被标记,那么同一条匹配边上的S侧的点必然也被标记——点集中不包含同一条匹配边上的两个点。S侧的未标记点,实际上是除去未匹配点,以及跟随T侧点而被标记的点后,剩余的(即选择了那些T侧未被标记的匹配边)。所以两侧的点把所有的匹配边都包含了。所以最大二分匹配数==最小覆盖点数。

      3、为什么这些点就是最小值?

      这个问题最简单:及使图上只有n条匹配边,我们都要用n个点才能覆盖。若再少,至少漏掉一条匹配边。所以是最小值。

    (注:难得自己动手改了张图...圆圈是覆盖点,方格是标记点,箭头是交错路径,蓝线是匹配边。)

    可以看看这个链接,我认为上面的解释更通俗一些。

    http://www.matrix67.com/blog/?s=%E6%9C%80%E5%B0%8F%E8%A6%86%E7%9B%96%E7%82%B9

      1 #include<cstdio>
      2 #include<cstring>
      3 #include<vector>
      4 #include<algorithm>
      5 #define clr(a,m) memset(a,m,sizeof(a))
      6 #define rep(i,a,b) for(int i=a;i<=b;i++)
      7 using namespace std;
      8 
      9 const int MAXN=1111;
     10 
     11 int n,m,cnt,ans;
     12 
     13 vector<int>G[MAXN];
     14 vector<int>row,col;
     15 
     16 int left[MAXN],right[MAXN],S[MAXN],T[MAXN];
     17 
     18 void init()
     19 {
     20     rep(i,1,n)
     21         G[i].clear();
     22 }
     23 
     24 void read()
     25 {
     26     int x,y;
     27     init();
     28     rep(i,1,cnt){
     29         scanf("%d%d",&x,&y);
     30         G[x].push_back(y);
     31     }
     32 }
     33 
     34 bool match(int u)
     35 {
     36     S[u]=true;
     37     int sz=G[u].size();
     38     rep(i,0,sz-1){
     39         int v=G[u][i];
     40         if(!T[v]){
     41             T[v]=true;
     42             if(!left[v]||match(left[v])){
     43                 left[v]=u;
     44                 right[u]=v;
     45                 return true;
     46             }
     47         }
     48     }
     49     return false;
     50 }
     51 
     52 void AP()
     53 {
     54     rep(i,1,m)left[i]=0;//n和m写反了
     55     rep(i,1,n)right[i]=0;
     56 
     57     ans=0;
     58     rep(i,1,n){
     59         rep(j,1,n)S[j]=0;
     60         rep(j,1,m)T[j]=0;
     61         if(match(i))
     62             ans++;
     63     }
     64 }
     65 
     66 void mincover()
     67 {
     68     rep(i,1,n)S[i]=0;//重新标记增广路
     69     rep(i,1,m)T[i]=0;
     70 
     71     rep(i,1,n)//选择S侧的未标记点
     72         if(!right[i])
     73             match(i);
     74 
     75     row.clear();
     76     col.clear();
     77     rep(i,1,n)
     78         if(!S[i])
     79             row.push_back(i);
     80     rep(i,1,m)
     81         if(T[i])
     82             col.push_back(i);
     83 }
     84 
     85 void print()
     86 {
     87     printf("%d",ans);
     88 int sz=col.size();
     89     rep(i,0,sz-1)
     90         printf(" c%d",col[i]);
     91      sz=row.size();
     92     rep(i,0,sz-1)
     93         printf(" r%d",row[i]);
     94 
     95 
     96 
     97     printf("
    ");
     98 }
     99 
    100 int main()
    101 {
    102     while(~scanf("%d%d%d",&n,&m,&cnt))
    103     {
    104         if(!n&&!m&&!cnt)
    105             return 0;
    106         read();
    107         AP();
    108         mincover();
    109         print();
    110     }
    111     return 0;
    112 }
    View Code
  • 相关阅读:
    【转】NSArray,NSSet,NSDictionary总结
    dequeueReusableCellWithIdentifier
    可任意自定义的UITableViewCell
    contentSize、contentInset和contentOffset区别
    Cocoa的MVC架构分析 delegate
    WP7 Toolkit ExpanderView 控件 介绍 02
    ObjectiveC中一种消息处理方法performSelector: withObject:
    [转]HTML5多点触摸演示源码(Canvas绘制演示)
    Matlab 积分图的快速计算
    测试
  • 原文地址:https://www.cnblogs.com/zstu-abc/p/3297239.html
Copyright © 2011-2022 走看看