日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【HDU 2255】奔小康赚大钱 (最佳二分匹配KM算法)

發布時間:2024/7/19 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【HDU 2255】奔小康赚大钱 (最佳二分匹配KM算法) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

?

奔小康賺大錢

Time Limit: 1000/1000 MS (Java/Others)????Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1836????Accepted Submission(s): 798


Problem Description 傳說在遙遠的地方有一個非常富裕的村落,有一天,村長決定進行制度改革:重新分配房子。
這可是一件大事,關系到人民的住房問題啊。村里共有n間房間,剛好有n家老百姓,考慮到每家都要有房住(如果有老百姓沒房子住的話,容易引起不安定因素),每家必須分配到一間房子且只能得到一間房子。
另一方面,村長和另外的村領導希望得到最大的效益,這樣村里的機構才會有錢.由于老百姓都比較富裕,他們都能對每一間房子在他們的經濟范圍內出一定的價格,比如有3間房子,一家老百姓可以對第一間出10萬,對第2間出2萬,對第3間出20萬.(當然是在他們的經濟范圍內).現在這個問題就是村領導怎樣分配房子才能使收入最大.(村民即使有錢購買一間房子但不一定能買到,要看村領導分配的).

?

Input 輸入數據包含多組測試用例,每組數據的第一行輸入n,表示房子的數量(也是老百姓家的數量),接下來有n行,每行n個數表示第i個村名對第j間房出的價格(n<=300)。

?

Output 請對每組數據輸出最大的收入值,每組的輸出占一行。

?

Sample Input 2 100 10 15 23

?

Sample Output 123

?

【分析】

  只是打一下模版。最佳二分匹配KM算法。

?

來來上代碼:

1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 #include<queue> 7 using namespace std; 8 #define Maxn 310 9 #define Maxm 160010 10 #define INF 0xfffffff 11 12 struct node 13 { 14 int x,y,c,next; 15 }t[Maxm];int len; 16 int first[Maxn]; 17 18 void ins(int x,int y,int c) 19 { 20 t[++len].x=x;t[len].y=y;t[len].c=c; 21 t[len].next=first[x];first[x]=len; 22 } 23 24 int mymin(int x,int y) {return x<y?x:y;} 25 int mymax(int x,int y) {return x>y?x:y;} 26 27 int n; 28 int match[Maxn],lx[Maxn],ly[Maxn];//lx,ly 可行頂標 29 bool visx[Maxn],visy[Maxn]; 30 int slack[Maxn]; 31 32 bool ffind(int x) 33 { 34 visx[x]=1; 35 for(int i=first[x];i;i=t[i].next) if(!visy[t[i].y]) 36 { 37 int y=t[i].y; 38 if(t[i].c==lx[x]+ly[y]) 39 { 40 visy[y]=1; 41 if(!match[y]||ffind(match[y]))//這里可以理解為有向的增廣路,只在一段記錄match 42 { 43 match[y]=x; 44 return 1; 45 } 46 } 47 else slack[y]=mymin(slack[y],lx[x]+ly[y]-t[i].c); 48 } 49 return 0; 50 } 51 52 void solve() 53 { 54 memset(match,0,sizeof(match)); 55 memset(lx,0,sizeof(lx)); 56 memset(ly,0,sizeof(ly)); 57 for(int i=1;i<=n;i++) 58 for(int j=first[i];j;j=t[j].next) lx[i]=mymax(lx[i],t[j].c); 59 60 for(int i=1;i<=n;i++) 61 { 62 for(int j=1;j<=n;j++) 63 slack[j]=INF; 64 while(1) 65 { 66 memset(visx,0,sizeof(visx)); 67 memset(visy,0,sizeof(visy)); 68 if(ffind(i)) break; 69 int delta=INF; 70 for(int j=1;j<=n;j++) 71 { 72 if(!visy[j]) 73 { 74 delta=mymin(delta,slack[j]); 75 } 76 } 77 if(delta==INF) return; 78 for(int j=1;j<=n;j++) 79 { 80 if(visx[j]) lx[j]-=delta; 81 if(visy[j]) ly[j]+=delta; 82 else slack[j]-=delta; 83 } 84 } 85 } 86 } 87 88 int main() 89 { 90 while(scanf("%d",&n)!=EOF) 91 { 92 len=0; 93 memset(first,0,sizeof(first)); 94 for(int i=1;i<=n;i++) 95 { 96 for(int j=1;j<=n;j++) 97 { 98 int x; 99 scanf("%d",&x); 100 ins(i,j,x); 101 } 102 } 103 solve(); 104 int ans=0; 105 for(int i=1;i<=n;i++) ans+=lx[i]+ly[i]; 106 printf("%d\n",ans); 107 } 108 return 0; 109 } HDU 2255

?

?


?

?

基礎:

?

二分圖:簡單來說,如果圖中點可以被分為兩組,并且使得所有邊都跨越組的邊界,則這就是一個二分圖。準確地說:把一個圖的頂點劃分為兩個不相交集?UU?和VV?,使得每一條邊都分別連接UU、VV中的頂點。如果存在這樣的劃分,則此圖為一個二分圖。二分圖的一個等價定義是:不含有「含奇數條邊的環」的圖。圖 1 是一個二分圖。為了清晰,我們以后都把它畫成圖 2 的形式。

匹配:在圖論中,一個「匹配」(matching)是一個邊的集合,其中任意兩條邊都沒有公共頂點。例如,圖 3、圖 4 中紅色的邊就是圖 2 的匹配。

??????

我們定義匹配點匹配邊未匹配點非匹配邊,它們的含義非常顯然。例如圖 3 中 1、4、5、7 為匹配點,其他頂點為未匹配點;1-5、4-7為匹配邊,其他邊為非匹配邊。

最大匹配:一個圖所有匹配中,所含匹配邊數最多的匹配,稱為這個圖的最大匹配。圖 4 是一個最大匹配,它包含 4 條匹配邊。

完美匹配:如果一個圖的某個匹配中,所有的頂點都是匹配點,那么它就是一個完美匹配。圖 4 是一個完美匹配。顯然,完美匹配一定是最大匹配(完美匹配的任何一個點都已經匹配,添加一條新的匹配邊一定會與已有的匹配邊沖突)。但并非每個圖都存在完美匹配。

舉例來說:如下圖所示,如果在某一對男孩和女孩之間存在相連的邊,就意味著他們彼此喜歡。是否可能讓所有男孩和女孩兩兩配對,使得每對兒都互相喜歡呢?圖論中,這就是完美匹配問題。如果換一個說法:最多有多少互相喜歡的男孩/女孩可以配對兒?這就是最大匹配問題。

基本概念講完了。求解最大匹配問題的一個算法是匈牙利算法,下面講的概念都為這個算法服務。

交替路:從一個未匹配點出發,依次經過非匹配邊、匹配邊、非匹配邊…形成的路徑叫交替路。

增廣路:從一個未匹配點出發,走交替路,如果途徑另一個未匹配點(出發的點不算),則這條交替路稱為增廣路(agumenting path)。例如,圖 5 中的一條增廣路如圖 6 所示(圖中的匹配點均用紅色標出):

增廣路有一個重要特點:非匹配邊比匹配邊多一條。因此,研究增廣路的意義是改進匹配。只要把增廣路中的匹配邊和非匹配邊的身份交換即可。由于中間的匹配節點不存在其他相連的匹配邊,所以這樣做不會破壞匹配的性質。交換后,圖中的匹配邊數目比原來多了 1 條。

我們可以通過不停地找增廣路來增加匹配中的匹配邊和匹配點。找不到增廣路時,達到最大匹配(這是增廣路定理)。匈牙利算法正是這么做的。在給出匈牙利算法 DFS 和 BFS 版本的代碼之前,先講一下匈牙利樹。

匈牙利樹一般由 BFS 構造(類似于 BFS 樹)。從一個未匹配點出發運行 BFS(唯一的限制是,必須走交替路),直到不能再擴展為止。例如,由圖 7,可以得到如圖 8 的一棵 BFS 樹:

???? ??

這棵樹存在一個葉子節點為非匹配點(7 號),但是匈牙利樹要求所有葉子節點均為匹配點,因此這不是一棵匈牙利樹。如果原圖中根本不含 7 號節點,那么從 2 號節點出發就會得到一棵匈牙利樹。這種情況如圖 9 所示(順便說一句,圖 8 中根節點 2 到非匹配葉子節點 7 顯然是一條增廣路,沿這條增廣路擴充后將得到一個完美匹配)。


?

?


?

KM算法(最佳完美匹配)

?

二分圖的最佳完美匹配


如果二分圖的每條邊都有一個權(可以是負數),要求一種完備匹配方案,使得所有匹配邊的權和最大,記做最佳完美匹配。(特殊的,當所有邊的權為1時,就是最大完備匹配問題)?
我們使用KM算法解決該問題。

KM(Kuhn and Munkres)算法,是對匈牙利算法的一種貪心擴展,如果對匈牙利算法還不夠明白,建議先重新回顧一下匈牙利算法。

KM是對匈牙利算法的一種貪心擴展,這種貪心不是對邊的權值的貪心,算法發明者引入了一些新的概念,從而完成了這種擴展。

?

可行頂標


對于原圖中的任意一個結點,給定一個函數L(node)求出結點的頂標值。我們用數組lx(x)記錄集合X中的結點頂標值,用數組ly(y)記錄集合Y中的結點頂標值。?
并且,對于原圖中任意一條邊edge(x,y),都滿足

lx(x)+ly(y)>=weight(x,y)

?

相等子圖


相等子圖是原圖的一個生成子圖(生成子圖即包含原圖的所有結點,但是不包含所有的邊),并且該生成子圖中只包含滿足

lx(x)+ly(y)=weight(x,y)

的邊,這樣的邊我們稱之為可行邊

?

算法原理

  • 定理:如果原圖的一個相等子圖中包含完備匹配,那么這個匹配就是原圖的最佳二分圖匹配。

  • 證明 :由于算法中一直保持頂標的可行性,所以任意一個匹配的權值之和肯定小于等于所有結點的頂標之和,則相等子圖中的完備匹配肯定是最優匹配。

這就是為什么我們要引入可行頂標相等子圖的概念。?
上面的證明可能太過抽象,我們結合圖示更直觀的表述。

該圖表示原圖,且X=1,2,3,Y=4,5,6,給出權值

weight(1,4)=5?
weight(1,5)=10?
weight(1,6)=15?
weight(2,4)=5?
weight(2,5)=10?
weight(3,4)=10?
weight(3,6)=20

對于原圖的任意一個匹配M

那么對于

edge(1,6)weight(1,6)=15?
edge(2,5)weight(2,5)=10?
edge(3,4)weight(3,4)=10

都滿足

lx(x)+ly(y)>=weight(x,y)

?

所以

i=1xiXlx(xi)+i=1yiYly(yi)=K>=weight(xi,yi)

?

可以看出,一個匹配中的邊權之和最大為K

那么很顯然,當一個匹配G?的邊權之和恰好為K時,那么G?就是二分圖的最佳完美匹配。

如果對于每一條邊edge(xi,yi)都滿足

lx(xi)+ly(yi)==weight(xi,yi)


那么

i=1xiXlx(xi)+i=1yiYly(yi)=K=weight(xi,yi)

?

相等子圖的完備匹配(完美匹配)即滿足上述條件(因為相等子圖的每條邊都是可行邊,可行邊滿足lx(xi)+ly(yi)=weight(xi,yi))所以當相等子圖有完備匹配的時候,原圖有最佳完美匹配。


?

KM的算法流程

流程


Kuhn-Munkras算法(即KM算法)流程:

  • 初始化可行頂標的值 (設定lx,ly的初始值)
  • 用匈牙利算法尋找相等子圖的完備匹配
  • 若未找到增廣路則修改可行頂標的值
  • 重復(2)(3)直到找到相等子圖的完備匹配為止
  • KM算法的核心部分即控制修改可行頂標的策略使得最終可到達一個完美匹配。

  • 初始時,設定lx[xi]為和xi相關聯的edge(xi,yj)的最大權值,ly[yj]=0,滿足公式lx[xi]+ly[yj]>=weight(xi,yj)
  • 當相等子圖中不包含完備匹配的時候(也就是說還有增廣路),就適當修改頂標。直到找到完備匹配為止。(整個過程在匈牙利算法中執行)
  • 現在我們的問題是,遵循什么樣的原則去修改頂標的值?

    對于正在增廣的增廣路徑上屬于集合X的所有點減去一個常數delta,屬于集合Y的所有點加上一個常數delta

    為什么要這樣做呢,我們來分析一下:?
    對于圖中任意一條邊edge(i,j)?(其中xiXxjY)權值為weight(i,j)

      1、如果i和j都屬于增廣路,那么lx[i]?delta+ly[j]?+delta=lx[i]+ly[j]值不變,也就說edge(i,j)可行性不變,原來是相等子圖的邊就還是,原來不是仍然不是
      2、如果i屬于增廣路,j不屬于增廣路,那么lx[i]?delta+ly[j]的值減小,也就是原來這條邊不在相等子圖中(否則j就會被遍歷到了),現在可能就會加入到相等子圖。
      3、如果i不屬于增廣路,j屬于增廣路,那么lx[i]+ly[j]+delta的值增大,也就是說原來這條邊不在相等子圖中(否則j就會被遍歷到了),現在還不可能加入到相等子圖
      4、如果i,j都不屬于增廣路,那么lx[i]和ly[j]都不會加減常數delta值不變,可行性不變

    ?

    //看成匈牙利樹更容易理解。

    你每次i++,選一個為匹配的x點進行增廣,找到以i為起點的匈牙利樹。

    如果長這樣:

    ,那么你就找到了增廣路,i的工作就完成了,i++即可,不需改變頂標。

    否則,你的匈牙利樹長這樣,

    你會把整棵樹完整的找一遍。我的意思是,能走到的點的vis值都會標記到的。

    上面說的“在增廣路上”實際上是“在匈牙利樹上”。

    解釋一下第2、3種情況。

    2、若x在匈牙利樹上,y不在,說明x->y并非相等子圖上的邊,不然因為找不到增廣路,相等子圖上的邊我都會遍歷一遍,所有點都會打上vis標記。

    3、若x不在匈牙利樹上,y在,x一定是i后面的點,它的匹配會在后面使其成立,所以可以看成不是相等子圖上的邊?【其實這個我還覺得有點迷吧。。但是頂標只是增加,所以不會錯的。

    //有slack的y必定有一條邊連向一個打過vis標記的點【看代碼這個就明白了】

    【以上是個人的傻逼見解啊。。2017-04-25 10:44:23】

    ?

    這 樣,在進行了這一步修改操作后,圖中原來的可行邊仍可行,而原來不可行的邊現在則可能變為可行邊。那么delta的值應取多少?

    觀察上述四種情況,只有第二類邊(xiX,yjY)的可行性經過修改可以改變。

    因為對于每條邊都要滿足lx(i)+ly(j)>=weight(i,j),這一性質絕對不可以改變,所以取第二種情況的?lx[i]+ly[j]?weight(i,j)的最小值作為delta

    證明 :?

    delta=Min(lx[i]+ly[j]?weight(i,j))=lx[i]+ly[j]?Max(weight(i,j))


    第二類邊 :?

    lx[i]?delta+ly[j]=lx[i]?lx[i]?ly[i]+Max(weight(i,j))+ly[j]=Max(weight)>=weight(i,j)


    成立

    ?

    下面我們重新回顧一下整個KM算法的流程 :

  • 可行頂標:每個點有一個標號,記(xiX,yjY)。如果對于圖中的任意邊edge(i,j)都有lx[i]+ly[j]>=weight(i,j),則這一頂標是可行的。特別地,對于lx[i]+ly[j]=weight(i,j),稱為可行邊(也就是相等子圖里的邊)
  • KM 算法的核心思想就是通過修改某些點的標號(但要滿足點標始終是可行的),不斷增加圖中的可行邊總數,直到圖中存在僅由可行邊組成的完全匹配為止,此時這個 匹配一定是最佳的(證明上文已經給出)
  • 初始化:lx[i]=Max(edge(i,j)),xiX,edge(i,j)Ely[j]=0。這個初始頂標顯然是可行的,并且,與任意一個X方點關聯的邊中至少有一條可行邊
  • 從每個X方點開始DFS增廣。DFS增廣的過程與最大匹配的Hungary算法基本相同,只是要注意兩點:一是只找可行邊,二是要把搜索過程中遍歷到的X方點全部記下來,以便進行后面的修改
  • 增廣的結果有兩種:若成功(找到了增廣路),則該點增廣完成,進入下一個點的增廣。若失敗(沒有找到增廣路),則需要改變一些點的標號,使得圖中可行邊的 數量增加。
  • 修改后,繼續對這個X方點DFS增廣,若還失敗則繼續修改,直到成功為止
  • ?


    ?


    ?

    下面用圖模擬:

    初始化標桿使X標桿的值為斜體字的值。?

    連接每條邊并且使得x1和y3匹配,然后遍歷x2,發現x2找不到合法增廣路。?

    把不合法路徑上的x點都歸為點集S,y點都歸為T,將不在T中的y點和在S中的點嘗試進行加邊。?

    找到兩條邊,更新頂標之后,成功形成增廣路,運用匈牙利算法反選。?

    給x3找一個合法的增廣路,一下就找到了,直接反選,結束。?





    ?

    KM算法的優化

    KM算法可以優化到O(n3)

    一個優化是對Y頂點引入松弛函數slackslack[j]保存跟當前節點j相連的節點ilx[i]+ly[j]?weight(i,j)的最小值,于是求delta時只需O(n)枚舉不在交錯樹中的Y頂點的最小slack值即可。

    松弛值可以在匈牙利算法檢查相等子樹邊失敗時進行更新,同時在修改標號后也要更新

    ?


    ?

    轉自:

    http://www.renfei.org/blog/bipartite-matching.html

    http://blog.csdn.net/sixdaycoder/article/details/47720471

    http://blog.csdn.net/zxn0803/article/details/49999267

    ?

    ?


    ?

    ?

    2016-10-26?21:12:20

    ?

    轉載于:https://www.cnblogs.com/Konjakmoyu/p/6001900.html

    總結

    以上是生活随笔為你收集整理的【HDU 2255】奔小康赚大钱 (最佳二分匹配KM算法)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 澳门三级 | 99精品久久久 | 小柔的裸露日记h | 精品在线不卡 | 三级网站在线免费观看 | 午夜剧场免费观看 | 国产精品资源站 | 亚洲六月丁香色婷婷综合久久 | 久久视频在线看 | 综合久久国产 | 成人午夜在线观看 | 黄色片网站在线免费观看 | 操人视频免费 | 成人精品一区二区三区电影 | 99久久久久久久 | 日韩精品视频在线看 | 日韩亚洲视频在线观看 | 国产日韩av在线播放 | 午夜影视福利 | 久久亚洲精品视频 | 中国一级免费毛片 | 天干夜天干天天天爽视频 | 中文字幕精品一区二区精品 | 四虎一区二区 | 亚洲性天堂 | 亚洲鲁鲁| 亚洲精品欧美日韩 | 久久久久久久综合 | 日韩欧美三级 | 国产51自产区| 欧美激情免费看 | 91亚洲视频在线观看 | 日本成人精品视频 | 狠狠干网站 | 七七色影院 | 久久精品tv | 亚洲少妇中文字幕 | 91av视频网 | 性欧美videos另类艳妇3d | 免费看裸体视频网站 | 日本在线资源 | 国产精品乱码一区二区视频 | 成年人黄视频 | 中出一区 | 色婷婷综合激情 | 亚洲视频在线播放免费 | h视频在线免费观看 | 在线视频观看你懂得 | www.国产在线 | 久久网站免费看 | 九九热这里有精品视频 | 中文字幕+乱码+中文乱码www | 午夜激情视频在线播放 | 国产女优在线播放 | 色二区| 亚洲香蕉 | 日韩av一区在线观看 | 黄色一级片黄色一级片 | 日日躁夜夜躁白天躁晚上躁91 | 日本真人做爰免费视频120秒 | 欧美一区二区三区婷婷月色 | 激情综合网av | 中文字幕第88页 | 日本无遮羞调教打屁股网站 | 少妇性高潮视频 | 瑟瑟网站免费 | 摸一摸操一操 | 一区二区三区视频免费 | 久久77| 狠狠插视频 | 国产一级一片 | 亚洲av无码专区国产乱码不卡 | 久久h视频 | 国产5区| 国产又爽又猛又粗的视频a片 | 69超碰| 99亚洲欲妇 | 欧美日韩精品三区 | 一区二区三区中文视频 | 麻豆精品国产传媒mv男同 | 日本人妻熟妇久久久久久 | 成人在线观看h | 亚洲狠狠 | 尤物国产 | 狠狠干av| 在线观看中文字幕第一页 | 国语对白做受xxxxx在线中国 | 欧美日韩毛片 | 亚洲国产欧美自拍 | 国产精品1区2区3区 在线看黄的网站 | 成年人免费看黄色 | 国模私拍一区二区三区 | 亚洲天堂导航 | 少妇日b| jzjzz成人免费视频 | 日本午夜一区二区 | 国产精品毛片久久久 | 尹人久久| 欧美性受xxxx狂喷水 |