• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            POJ 1496 C++ (圖論)

            //匈牙利算法實現二分圖的最大匹配,較最大流實現來的簡單些
            //特點不需要建模,原理還是樸素最大流原理,尋找增廣路徑用DFS,如找到一條增廣路徑,沿路邊取反

            #include<iostream>
            using namespace std;
            int n,m,res;
            int l[300],r[300],map[300][300],used[300];

            bool path(int u)
            {int v;
            for(v=0;v<m;v++)
                 if(map[u][v] && !used[v])
                   {used[v]=1;
                    if(r[v]==-1 || path(r[v]))
                       {  l[u]=v;
                          r[v]=u;
                          return true;
                       }
                   }
               return false;        
            }

            void solve()
            { int i;
              memset(l,-1,sizeof(l));
              memset(r,-1,sizeof(r));
              for(i=0;i<n;i++)
                   if(l[i]==-1)
                       {  memset(used,0,sizeof(used));
                          if(path(i))
                             res++;
                        }  

            }

            int main()
            { int Case,i,j,k,temp;
            freopen("in.txt","r",stdin);
            freopen("out.txt","w",stdout);
              scanf("%d",&Case);
              while(Case--)
                   { memset(map,0,sizeof(map));
                     scanf("%d%d",&n,&m);
                     for(i=0;i<n;i++)
                         { scanf("%d",&k);
                           for(j=0;j<k;j++)
                               {scanf("%d",&temp);
                                map[i][temp-1]=1;
                               }
                           }
                       res=0;    
                       solve();
                       if(res==n)
                          printf("YES\n");
                       else
                         printf("NO\n");            
                     }
               return 0;      
            }
                          




            什么是二分圖,什么是二分圖的最大匹配,這些定義我就不講了,網上隨便都找得到。二分圖的最大匹配有兩種求法,第一種是最大流(我在此假設讀者已有網絡流的知識);第二種就是我現在要講的匈牙利算法。這個算法說白了就是最大流的算法,但是它跟據二分圖匹配這個問題的特點,把最大流算法做了簡化,提高了效率。匈牙利算法其實很簡單,但是網上搜不到什么說得清楚的文章。所以我決定要寫一下。
            最大流算法的核心問題就是找增廣路徑(augment path)。匈牙利算法也不例外,它的基本模式就是:

            初始時最大匹配為空
            while 找得到增廣路徑
                do 把增廣路徑加入到最大匹配中去
            可見和最大流算法是一樣的。但是這里的增廣路徑就有它一定的特殊性,下面我來分析一下。
            (注:匈牙利算法雖然根本上是最大流算法,但是它不需要建網絡模型,所以圖中不再需要源點和匯點,僅僅是一個二分圖。每條邊也不需要有方向。)


            圖1 圖2

            圖1是我給出的二分圖中的一個匹配:[1,5]和[2,6]。圖2就是在這個匹配的基礎上找到的一條增廣路徑:3->6->2->5->1->4。我們借由它來描述一下二分圖中的增廣路徑的性質:

            (1)有奇數條邊。
            (2)起點在二分圖的左半邊,終點在右半邊。
            (3)路徑上的點一定是一個在左半邊,一個在右半邊,交替出現。(其實二分圖的性質就決定了這一點,因為二分圖同一邊的點之間沒有邊相連,不要忘記哦。)
            (4)整條路徑上沒有重復的點。
            (5)起點和終點都是目前還沒有配對的點,而其它所有點都是已經配好對的。(如圖1、圖2所示,[1,5]和[2,6]在圖1中是兩對已經配好對的點;而起點3和終點4目前還沒有與其它點配對。)
            (6)路徑上的所有第奇數條邊都不在原匹配中,所有第偶數條邊都出現在原匹配中。(如圖1、圖2所示,原有的匹配是[1,5]和[2,6],這兩條配匹的邊在圖2給出的增廣路徑中分邊是第2和第4條邊。而增廣路徑的第1、3、5條邊都沒有出現在圖1給出的匹配中。)
            (7)最后,也是最重要的一條,把增廣路徑上的所有第奇數條邊加入到原匹配中去,并把增廣路徑中的所有第偶數條邊從原匹配中刪除(這個操作稱為增廣路徑的取反),則新的匹配數就比原匹配數增加了1個。(如圖2所示,新的匹配就是所有藍色的邊,而所有紅色的邊則從原匹配中刪除。則新的匹配數為3。)

            不難想通,在最初始時,還沒有任何匹配時,圖1中的兩條灰色的邊本身也是增廣路徑。因此在這張二分圖中尋找最大配匹的過程可能如下:

            (1)找到增廣路徑1->5,把它取反,則匹配數增加到1。
            (2)找到增廣路徑2->6,把它取反,則匹配數增加到2。
            (3)找到增廣路徑3->6->2->5->1->4,把它取反,則匹配數增加到3。
            (4)再也找不到增廣路徑,結束。

            當然,這只是一種可能的流程。也可能有別的找增廣路徑的順序,或者找到不同的增廣路徑,最終的匹配方案也可能不一樣。但是最大匹配數一定都是相同的。

            對于增廣路徑還可以用一個遞歸的方法來描述。這個描述不一定最準確,但是它揭示了尋找增廣路徑的一般方法:
            “從點A出發的增廣路徑”一定首先連向一個在原匹配中沒有與點A配對的點B。如果點B在原匹配中沒有與任何點配對,則它就是這條增廣路徑的終點;反之,如果點B已與點C配對,那么這條增廣路徑就是從A到B,再從B到C,再加上“從點C出發的增廣路徑”。并且,這條從C出發的增廣路徑中不能與前半部分的增廣路徑有重復的點。

            比如圖2中,我們要尋找一條從3出發的增廣路徑,要做以下3步:
            (1)首先從3出發,它能連到的點只有6,而6在圖1中已經與2配對,所以目前的增廣路徑就是3->6->2再加上從2出發的增廣路徑。
            (2)從2出發,它能連到的不與前半部分路徑重復的點只有5,而且5確實在原匹配中沒有與2配對。所以從2連到5。但5在圖1中已經與1配對,所以目前的增廣路徑為3->6->2->5->1再加上從1出發的增廣路徑。
            (3)從1出發,能連到的不與自已配對并且不與前半部分路徑重復的點只有4。因為4在圖1中沒有與任何點配對,所以它就是終點。所以最終的增廣路徑是3->6->2->5->1->4。

            但是嚴格地說,以上過程中從2出發的增廣路徑(2->5->1->4)和從1出發的增廣路徑(1->4)并不是真正的增廣路徑。因為它們不符合前面講過的增廣路徑的第5條性質,它們的起點都是已經配過對的點。我們在這里稱它們為“增廣路徑”只是為了方便說明整個搜尋的過程。而這兩條路徑本身只能算是兩個不為外界所知的子過程的返回結果。
            顯然,從上面的例子可以看出,搜尋增廣路徑的方法就是DFS,可以寫成一個遞歸函數。當然,用BFS也完全可以實現。

            至此,理論基礎部份講完了。但是要完成匈牙利算法,還需要一個重要的定理:

            如果從一個點A出發,沒有找到增廣路徑,那么無論再從別的點出發找到多少增廣路徑來改變現在的匹配,從A出發都永遠找不到增廣路徑。

            要用文字來證明這個定理很繁,話很難說,要么我還得多畫一張圖,我在此就省了。其實你自己畫幾個圖,試圖舉兩個反例,這個定理不難想通的。(給個提示。如果你試圖舉個反例來說明在找到了別的增廣路徑并改變了現有的匹配后,從A出發就能找到增廣路徑。那么,在這種情況下,肯定在找到別的增廣路徑之前,就能從A出發找到增廣路徑。這就與假設矛盾了。)
            有了這個定理,匈牙利算法就成形了。如下:
            初始時最大匹配為空
            for 二分圖左半邊的每個點i
                do 從點i出發尋找增廣路徑。如果找到,則把它取反(即增加了總了匹配數)。

            如果二分圖的左半邊一共有n個點,那么最多找n條增廣路徑。如果圖中共有m條邊,那么每找一條增廣路徑(DFS或BFS)時最多把所有邊遍歷一遍,所花時間也就是m。所以總的時間大概就是O(n * m)。

            posted on 2008-11-29 13:08 蝸牛 閱讀(1317) 評論(0)  編輯 收藏 引用 所屬分類: ACM ICPC

            <2008年11月>
            2627282930311
            2345678
            9101112131415
            16171819202122
            23242526272829
            30123456

            導航

            統計

            常用鏈接

            留言簿(1)

            隨筆分類(20)

            隨筆檔案(20)

            Favorites

            搜索

            最新評論

            閱讀排行榜

            評論排行榜

            久久99国产综合精品| 久久综合亚洲色一区二区三区| 久久精品国产亚洲AV不卡| 久久精品国产亚洲av麻豆蜜芽| 人妻无码久久一区二区三区免费| 欧美精品久久久久久久自慰| 成人综合伊人五月婷久久| 国产精品日韩深夜福利久久| 精品久久久久成人码免费动漫| 久久久国产乱子伦精品作者| 久久久99精品一区二区| 亚洲国产另类久久久精品小说| 四虎国产精品免费久久5151 | 久久99热这里只有精品国产| 久久人人青草97香蕉| 国产精品99久久免费观看| 亚洲国产成人久久一区久久| 久久久久亚洲av无码专区| 欧美亚洲国产精品久久高清| 国产午夜电影久久| 国产午夜精品久久久久免费视| 久久只有这精品99| 国产免费久久久久久无码| 97热久久免费频精品99| 色婷婷噜噜久久国产精品12p| 9191精品国产免费久久| 久久人人爽人人爽人人片av高请 | 7777久久久国产精品消防器材| 国产成人久久777777| 国产精品一区二区久久不卡| 亚洲色欲久久久综合网| 久久人妻少妇嫩草AV蜜桃| 免费精品久久久久久中文字幕| 精品久久久久久国产牛牛app| 99久久精品国产高清一区二区 | 久久精品国产亚洲av麻豆色欲| 亚洲欧美成人综合久久久| 亚洲乱码精品久久久久..| 久久精品无码专区免费青青| 亚洲狠狠婷婷综合久久蜜芽| 久久亚洲日韩精品一区二区三区|