zoukankan      html  css  js  c++  java
  • poj 3041 Asteroids (二分图 匈牙利算法)

    http://poj.org/problem?id=3041

    题意:

    给你N*N的矩阵,里面有的方格里有小行星,你需要用激光射掉它。。。激光可以射掉一行 或者一列的小行星,问最小需要发射多少次

    这道题 很久 以前就做过了  ,现在有做了 一下 ,对 匈牙利 有 个 更好的了解。。。

    转自 别处 :

    匈牙利算法是寻找最大匹配的优秀算法,那么与这个看上去一点也不像二分图的题来说有什么用处呢?让我们来做一个尝试:把样例数据里面的横坐标作为二分图的一部,纵坐标作为二分图的另一部,坐标为(x, y)的小行星表示为从横坐标x到纵坐标y的一段弧,就有了下图:
     

    可以看出,原问题变成了下面这个问题:给定一个二分图G = (V, E),定义一个点如果被覆盖,那么称所有与这个点相邻的弧被覆盖。求出最少需要覆盖多少个点才能覆盖所有的边。如上两个图,原问题中在x = 1和y = 2两处使用超级武器,等价于在右图中覆盖左边的点1和右边的点2。我们称这个问题的答案为最小覆盖数P,显然在样例中P = 2。

    现在我们给出结论,若将二分图最大匹配的边集设为M,则P = | M |。也就是最大匹配数。证明如下:

    首先证明P ≥ | M | 。对于M中的每一条边,它们都不相邻。那么至少需要覆盖 | M | 个点才能覆盖M中的所有边。故有P ≥ | M | 。

    现在给出一种令P = | M |的覆盖方法。如果当前图中对于所有的v ∈ V,都有v ∈ M,显然有P = | M |。否则,任取一条边v0,使v0 ∈ V且v0 ∉ M。那么显然v0的两个端点至少有一个被匹配。

    如 果有一端被匹配,设这个端点为a0。首先覆盖a0,然后观察与a0匹配的点a1的状况。如果存在与a1相邻的边v1,满足v1 ∈ V且v1 ∉ M,则v1一定不与没有匹配过的点相邻,否则,v0,v(a0, a1)及v1就形成了增广路。现在继续覆盖与v1相邻的另外一个点a2,然后依次操作,直到新连接到的匹配过的点不与V \ M中的边相邻为止。

    如果两端都被匹配,就向两个方向进行同样的操作。从v0的一系列操作结束之后,考察还没有被覆盖的边,继续进行操作。这样最后所有的边一定会被全部覆盖。

    如 果说有没有被覆盖到的边,这条边不可能同时与两个匹配过的点相邻。如果这样,在刚才的过程中一定会检索到这条边。所以说它必然是与一个匹配过的点ax相 邻,与一个没有匹配过的点ar相邻,并且ax的匹配点ay一定被覆盖过。那么,从ay向上找寻刚才的过程遍历过的边,与最后的v(ax, ar)一定会形成一条增广路,矛盾。

    观察到每条M中的边都有且仅有一个端点被覆盖,所以说P = | M |。

    由于P ≥ | M | 且存在P = | M |的状况,故P = | M | ,证明完毕。也就是说,这个题只需要用匈牙利算法求出最大匹配数就可以了。

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<cmath>
     4 #include<iostream>
     5 #include<algorithm>
     6 #include<set>
     7 #include<map>
     8 #include<queue>
     9 #include<vector>
    10 #include<string>
    11 #define Min(a,b) a<b?a:b
    12 #define Max(a,b) a>b?a:b
    13 #define CL(a,num) memset(a,num,sizeof(a));
    14 #define eps  1e-12
    15 #define inf   0x7fffffff
    16 
    17 //freopen("data.txt","r",stdin);
    18 const double pi  = acos(-1.0);
    19 typedef   __int64  ll;
    20 const int maxn = 510 ;
    21 using namespace std;
    22 int n , m;
    23 int mat[maxn][maxn],result[maxn],vis[maxn] ;
    24 void init()
    25 {
    26     memset(mat,0,sizeof(mat));
    27     memset(result,0,sizeof(result));
    28 }
    29 int find(int a)
    30 {
    31     int i;
    32     for(i=1;i<=n;i++)
    33     {
    34         if(mat[a][i]&&!vis[i])
    35         {
    36             vis[i]=1;
    37             if(result[i]==0||find(result[i]))
    38             {
    39                 result[i]=a;
    40                 return 1;
    41             }
    42         }
    43     }
    44     return 0;
    45 }
    46 int main()
    47 {
    48     int  i ,x,y ;
    49     while(scanf("%d%d",&n,&m)!=EOF)
    50     {
    51         init() ;
    52         for(i = 0 ; i < m;i++)
    53         {
    54             scanf("%d%d",&x,&y);
    55             mat[x][y] = 1;
    56 
    57         }
    58         int ans = 0 ;
    59         for(i = 1 ; i<=n;i++ )
    60         {
    61             CL(vis,0);
    62             if(find(i))ans ++ ;
    63         }
    64         printf("%d\n",ans) ;
    65 
    66     }
    67 }
  • 相关阅读:
    虚拟机里的mysql怎么外连
    Pytest跳过执行之@pytest.mark.skip()详解大全
    判断字符是什么,返回True或者False
    s = "ajldjlajfdljfddd",去重并从小到大排序输出"adfjl"(sort与sorted、reverse与reversed的区别)
    1、输入一个姓名,判断是否姓王 2、strip和replace的用法
    python 运算符
    控制语句--while循环
    控制语句-if
    函数
    控制语句--for循环
  • 原文地址:https://www.cnblogs.com/acSzz/p/2683924.html
Copyright © 2011-2022 走看看