简述二分图
二分圖又稱作二部圖,是圖論中的一種特殊模型。 設G=(V,E)是一個無向圖,如果頂點V可分割為兩個互不相交的子集(A,B),并且圖中的每條邊(i,j)所關聯的兩個頂點i和j分別屬于這兩個不同的頂點集(i in A,j in B),則稱圖G為一個二分圖。
簡而言之,就是頂點集V可分割為兩個互不相交的子集,并且圖中每條邊依附的兩個頂點都分屬于這兩個互不相交的子集。
區別二分圖,關鍵是看點集是否能分成兩個獨立的點集。
上圖是一個二分圖。
上圖不是一個二分圖。
我們可以用染色法判斷一個無向圖是否是二分圖;
從其中一個頂點開始,將跟它鄰接的點染成與其不同的顏色,如果鄰接的點有相同顏色的,則說明不是二分圖,每次用bfs遍歷即可。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | bool BFS(int s,int n) { ????queue<int >p; ????p.push(s); ????col[s]=1; ????while(!p.empty()) { ????????int from=p.front(); ????????p.pop(); ????????for(int i=1; i<=n; i++) { ????????????if(g[from][i]&&col[i]==-1) { ????????????????p.push(i); ????????????????col[i]=!col[from]; ????????????} ????????????if(g[from][i]&&col[from]==col[i]) { ????????????????return false; ????????????} ????????} ????} ????return true; } |
匹配:
給定一個二分圖G,在G的一個子圖M中,M的邊集中的任意兩條邊都不依附于同一個頂點,則稱M是一個匹配。
最大匹配:
給定一個二分圖G,在G的一個子圖M中,M的邊集中的任意兩條邊都不依附于同一個頂點,則稱M是一個匹配.
選擇這樣的邊數最大的子集稱為圖的最大匹配問題(maximal matching problem)
如果一個匹配中,圖中的每個頂點都和圖中某條邊相關聯,則稱此匹配為完全匹配,也稱作完備匹配.
如圖所示,圖中的最大匹配是4。
求最大匹配最常用的算法之一是匈牙利算法。匈牙利算法的核心是尋找增廣路徑。
**增廣路徑:
若P是圖G中一條連通兩個未匹配頂點的路徑,并且屬于M的邊和不屬于M的邊(即已匹配和待匹配的邊)在P上交替出現,則稱P為相對于M的一條增廣路徑(舉例來說,有A、B集合,增廣路由A中一個點通向B中一個點,再由B中這個點通向A中一個點……交替進行)。
**由增廣路的定義可以推出下述三個結論:
1-P的路徑長度必定為奇數,第一條邊和最后一條邊都不屬于M。
2-不斷尋找增廣路可以得到一個更大的匹配M’,直到找不到更多的增廣路。
3-M為G的最大匹配當且僅當不存在M的增廣路徑。
匈牙利算法的思路是不停的找增廣軌,并增加匹配的個數,增廣軌顧名思義是指一條可以使匹配數變多的路徑,在匹配問題中,增廣軌的表現形式是一條”交錯軌”,也就是說這條由圖的邊組成的路徑,它的第一條邊是目前還沒有參與匹配的,第二條邊參與了匹配,第三條邊沒有..最后一條邊沒有參與匹配,并且始點和終點還沒有被選擇過.這樣交錯進行,顯然他有奇數條邊.那么對于這樣一條路徑,我們可以將第一條邊改為已匹配,第二條邊改為未匹配…以此類推.也就是將所有的邊進行”反色”,容易發現這樣修改以后,匹配仍然是合法的,但是匹配數增加了一對.另外,單獨的一條連接兩個未匹配點的邊顯然也是交錯軌.可以證明,當不能再找到增廣軌時,就得到了一個最大匹配.
時間復雜度為:O(n*m)n是其中一個頂點集的個數,m是另一個的個數。
空間復雜度是O(n^2)。
代碼參見小康學長模板的52頁。
注:Hopcroft-Karp算法是對匈牙利算法的改進,時間復雜度為O(E*V^1/2);
算法實質其實就是匈牙利算法求增廣路,改進的地方是在深度搜索增廣路前,先通過廣度搜索,查找多條可以增廣的路線,從而不再讓dfs“一意孤行”。其中算法用到了分層標記防止多條增廣路重疊。
另一種(我個人覺得比較難的)的求最大匹配的算法是最大流。
求網絡流的最大流算法是Ford-Fulkerson算法。首先定義一條邊的殘留容量是容量減去現有流,由殘留容量標示的網絡稱為殘留網絡。再定義增廣路徑是殘留網絡中從源點s到匯點t的一條路徑。Ford-Fulkerson算法是一個迭代過程。每次在殘留網絡中查找一條增廣路徑,然后把這條增廣路徑中最小的一段殘留容量加到增廣路徑各條邊的流上,更新殘留網絡繼續迭代,直到找不到增廣路徑為止。此時退出迭代,現有流就是最大流。
二分圖有兩部分節點L和R,各部分內部節點之間沒有邊,即每條邊的兩個節點都一定分屬這兩部分,二分圖的一個匹配是找到這樣一組邊,使得每個節點都只有至多一條邊與其相連。
二分圖的最大匹配問題可以轉化為網絡最大流問題。增加一個到所有L中頂點容量均為1的源點s和一個所有R中頂點到其容量均為1的匯點t,所有L到R中的邊容量也設置為1,現在查找此網絡流的最大流就等同于求此二分圖的最大匹配。
poj 2536
http://poj.org/problem?id=2536
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | /*網絡流的做法*/ #include <iostream> #include <vector> #include <queue> #include <cmath> #include <cstring> using namespace std; #define N 205 #define MAX 1000000 int capacity[N][N] = {0}; int r_capcity[N][N] = {0}; int fluid[N][N] = {0}; int flag_for_bfs[N][N] = {0}; int s,t; int bfs_findP(vector<int>& p,int numV) { ????p.clear(); ????queue<int> q; ????int checked[N] = {0}; ????q.push(s); ????checked[s] = 1; ????int pre[N]; ????memset(pre,-1,sizeof(pre)/sizeof(int)); ????int cur; ????while (!q.empty()) { ????????cur = q.front(); ????????q.pop(); ????????if (cur == t) { ????????????break; ????????} ?? ????????for (int i = 0; i < numV; i++) { ????????????if (checked[i] == 0 && cur != i && r_capcity[cur][i] > 0) { ????????????????q.push(i); ????????????????checked[i] = 1; ????????????????pre[i] = cur; ????????????} ????????} ????} ????if (cur != t) { ????????return -1; ????} ????int i = pre[cur]; ????p.insert(p.begin(),cur); ????while (i != s) { ????????p.insert(p.begin(),i); ????????i = pre[i]; ????} ????return 0; } ?int max_fluid(int numV) { ????for (int i = 0; i < numV; i++) { ????????for (int j = 0; j < numV; j++) { ????????????fluid[i][j] = 0; ????????????r_capcity[i][j] = capacity[i][j] - fluid[i][j]; ????????} ????} ????vector<int> p; ????int r_cap = MAX; ????int key_i = -1,key_j = -1; ????while (bfs_findP(p,numV) != -1) { ????????r_cap = r_capcity[s][p[0]]; ????????for (int i = 0; i < p.size()-1; i++) { ????????????if (r_capcity[p[i]][p[i+1]] < r_cap) { ????????????????r_cap = r_capcity[p[i]][p[i+1]]; ????????????????key_i = p[i]; ????????????????key_j = p[i+1]; ????????????} ????????} ????????fluid[s][p[0]] += r_cap; ????????fluid[p[0]][s] = -fluid[s][p[0]]; ????????for (int i = 0; i < p.size()-1; i++) { ????????????fluid[p[i]][p[i+1]] = fluid[p[i]][p[i+1]] + r_cap; ????????????fluid[p[i+1]][p[i]] = -fluid[p[i]][p[i+1]]; ????????} ????????for (int i = 0; i < numV; i++) { ????????????for (int j = 0; j < numV; j++) { ????????????????r_capcity[i][j] = capacity[i][j] - fluid[i][j]; ????????????} ????????} ????} ????int ans = 0; ????for (int i = 0; i < numV; i++) { ????????if (s != i && fluid[s][i] > 0) { ????????????ans += fluid[s][i]; ????????} ????} ????return ans; } ?double distance(double x1,double y1,double x2,double y2) { ????return sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)); } ?int main() { ????int n,m,S,V; ????double gophers[105][2],holes[105][2]; ?? ????while (cin>>n) { ????????cin>>m>>S>>V; ????????for (int i = 0; i < n; i++) { ????????????cin>>gophers[i][0]>>gophers[i][1]; ????????} ????????for (int i = 0; i < m; i++) { ????????????cin>>holes[i][0]>>holes[i][1]; ????????} ????????for (int i = 0; i < N; i++) { ????????????for (int j = 0; j < N; j++) { ????????????????capacity[i][j] = 0; ????????????} ????????} ????????int numV = m + n + 2; ????????s = 0; ????????t = m + n + 1; ????????for (int i = 1; i <= n; i++) { ????????????capacity[0][i] = 1; ????????} ????????for (int i = n+1; i <= n+m; i++) { ????????????capacity[i][t] = 1; ????????} ????????for (int i = 1; i <= n; i++) { ????????????for (int j = n+1; j <= n+m; j++) { ????????????????if (distance(gophers[i-1][0],gophers[i-1][1],holes[j-n-1][0],holes[j-n-1][1]) <= V*S) { ????????????????????capacity[i][j] = 1; ????????????????} ????????????} ????????} ????????cout<<n - max_fluid(numV)<<endl; ????} ????return 0; } |
下面再來講一下:最大匹配&最小邊覆蓋&最小路徑覆蓋&最小頂點覆蓋&最大獨立集&最大團之間的關系。
【無向圖的最大獨立數】: 從V個頂點中選出k個頂,使得這k個頂互不相鄰。那么最大的k就是這個圖的最大獨立數。
【無向圖的最大團】:? 從V個頂點選出k個頂,使得這k個頂構成一個完全圖,即該子圖任意兩個頂都有直接的邊。
【最小路徑覆蓋(原圖不一定是二分圖,但必須是有向圖,拆點構造二分圖)】:在圖中找一些路徑,使之覆蓋了圖中的所有頂點,且任何一個頂點有且只有一條路徑與之關聯。最小路徑覆蓋 = |V| – 最大匹配數
【最小邊覆蓋(原圖是二分圖)】:在圖中找一些邊,使之覆蓋了圖中所有頂點,且任何一個頂點有且只有一條邊與之關聯。最小邊覆蓋 = 最大獨立集 = |V| – 最大匹配數
【最小頂點覆蓋】:用最少的點(左右兩邊集合的點)讓每條邊都至少和其中一個點關聯。
性質:
最大團 = 補圖的最大獨立集=|V|-|最大匹配數|(補圖);
最小邊覆蓋 = 二分圖最大獨立集 = |V| – 最小頂點覆蓋=|V|-最大匹配數
最小路徑覆蓋 = |V| – 最大匹配數
最小頂點覆蓋 = 最大匹配數
最小頂點覆蓋 + 最大獨立數 = |V|
最小割 = 最小點權覆蓋集 = 點權和 – 最大點權獨立集
求最大權匹配問題要用到KM算法。(詳見小康學長模板的53頁).
以下是一些例題:
1、Poj_1274(求最大匹配)
Farmer John completed his new barn just last week, complete with all the latest milking technology. Unfortunately, due to engineering problems, all the stalls in the new barn are different. For the first week, Farmer John randomly assigned cows to stalls, but it quickly became clear that any given cow was only willing to produce milk in certain stalls. For the last week, Farmer John has been collecting data on which cows are willing to produce milk in which stalls. A stall may be only assigned to one cow, and, of course, a cow may be only assigned to one stall.
Given the preferences of the cows, compute the maximum number of milk-producing assignments of cows to stalls that is possible.
Input
The input includes several cases. For each case, the first line contains two integers, N (0 <= N <= 200) and M (0 <= M <= 200). N is the number of cows that Farmer John has and M is the number of stalls in the new barn. Each of the following N lines corresponds to a single cow. The first integer (Si) on the line is the number of stalls that the cow is willing to produce milk in (0 <= Si <= M). The subsequent Si integers on that line are the stalls in which that cow is willing to produce milk. The stall numbers will be integers in the range (1..M), and no stall will be listed twice for a given cow.
Output
For each case, output a single line with a single integer, the maximum number of milk-producing stall assignments that can be made.
Sample Input
5 5
2 2 5
3 2 3 4
2 1 5
3 1 2 5
1 2
Sample Output
4
代碼:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | #include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using namespace std; const int N=300; int n1,n2; int _map[N][N]; bool vis[N]; int llink[N]; int find(int x) { ????int i; ????for(i=1; i<=n2; i++) { ????????if(_map[x][i]&&!vis[i]) { ????????????vis[i]=true; ????????????if(llink[i]==-1||find(llink[i])) { ????????????????llink[i]=x; ????????????????return true; ????????????} ????????} ????} ????return false; } int mach() { ????int ans=0; ????memset(llink,-1,sizeof(llink)); ????for(int i=1; i<=n1; i++) { ????????memset(vis,false,sizeof(vis)); ????????if(find(i)) { ????????????ans++; ????????} ????} ????return ans; } int main() { ????int n,m; ????int num; ????int b; ????while(~scanf("%d%d",&n,&m)) { ????????memset(_map,0,sizeof(_map)); ????????for(int i=1; i<=n; i++) { ????????????scanf("%d",&num); ????????????for(int j=0; j<num; j++) { ????????????????scanf("%d",&b); ????????????????_map[i][b]=1; ????????????} ????????} ????????n1=n; ????????n2=m; ????????printf("%d\n",mach()); ????} ????return 0; } |
2、poj_1325(最小頂點覆蓋,也就是最大匹配(開始時機器是處于開機狀態的,都是mode_0))
As we all know, machine scheduling is a very classical problem in computer science and has been studied for a very long history. Scheduling problems differ widely in the nature of the constraints that must be satisfied and the type of schedule desired. Here we consider a 2-machine scheduling problem.
There are two machines A and B. Machine A has n kinds of working modes, which is called mode_0, mode_1, …, mode_n-1, likewise machine B has m kinds of working modes, mode_0, mode_1, … , mode_m-1. At the beginning they are both work at mode_0.
For k jobs given, each of them can be processed in either one of the two machines in particular mode. For example, job 0 can either be processed in machine A at mode_3 or in machine B at mode_4, job 1 can either be processed in machine A at mode_2 or in machine B at mode_4, and so on. Thus, for job i, the constraint can be represent as a triple (i, x, y), which means it can be processed either in machine A at mode_x, or in machine B at mode_y.
Obviously, to accomplish all the jobs, we need to change the machine’s working mode from time to time, but unfortunately, the machine’s working mode can only be changed by restarting it manually. By changing the sequence of the jobs and assigning each job to a suitable machine, please write a program to minimize the times of restarting machines.
Input
The input file for this program consists of several configurations. The first line of one configuration contains three positive integers: n, m (n, m < 100) and k (k < 1000). The following k lines give the constrains of the k jobs, each line is a triple: i, x, y.
The input will be terminated by a line containing a single zero.
Output
The output should be one integer per line, which means the minimal times of restarting machine.
Sample Input
5 5 10
0 1 1
1 1 2
2 1 3
3 1 4
4 2 1
5 2 2
6 2 3
7 2 4
8 3 3
9 4 3
0
Sample Output
3
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | #include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using namespace std; const int N=1006; int n1,n2; int _map[N][N]; bool vis[N]; int llink[N]; int find(int x) { ????int i; ????for(i=0; i<n2; i++) { ????????if(_map[x][i]&&!vis[i]) { ????????????vis[i]=true; ????????????if(llink[i]==-1||find(llink[i])) { ????????????????llink[i]=x; ????????????????return true; ????????????} ????????} ????} ????return false; } int mach() { ????int ans=0; ????memset(llink,-1,sizeof(llink)); ????for(int i=0; i<n1; i++) { ????????memset(vis,false,sizeof(vis)); ????????if(find(i)) { ????????????ans++; ????????} ????} ????return ans; } int main() { ????int n,m; ????int k; ????int job,x,y; ????while(~scanf("%d",&n)) { ????????if(n==0) { ????????????break; ????????} ????????scanf("%d%d",&m,&k); ????????memset(_map,0,sizeof(_map)); ?? ????????for(int i=1; i<=k; i++) { ????????????scanf("%d %d %d",&job,&x,&y); ????????????if(x!=0&&y!=0) { ????????????????_map[x][n+y]=1; ????????????} ????????} ????????n1=n; ????????n2=m+n; ????????int ans1=mach(); ????????printf("%d\n",ans1);; ????} ????return 0; } |
3、poj_1422(最大獨立集)
Air Raid
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 6721 Accepted: 4001
Description
Consider a town where all the streets are one-way and each street leads from one intersection to another. It is also known that starting from an intersection and walking through town’s streets you can never reach the same intersection i.e. the town’s streets form no cycles.
With these assumptions your task is to write a program that finds the minimum number of paratroopers that can descend on the town and visit all the intersections of this town in such a way that more than one paratrooper visits no intersection. Each paratrooper lands at an intersection and can visit other intersections following the town streets. There are no restrictions about the starting intersection for each paratrooper.
Input
Your program should read sets of data. The first line of the input file contains the number of the data sets. Each data set specifies the structure of a town and has the format:
no_of_intersections
no_of_streets
S1 E1
S2 E2
……
Sno_of_streets Eno_of_streets
The first line of each data set contains a positive integer no_of_intersections (greater than 0 and less or equal to 120), which is the number of intersections in the town. The second line contains a positive integer no_of_streets, which is the number of streets in the town. The next no_of_streets lines, one for each street in the town, are randomly ordered and represent the town’s streets. The line corresponding to street k (k <= no_of_streets) consists of two positive integers, separated by one blank: Sk (1 <= Sk <= no_of_intersections) – the number of the intersection that is the start of the street, and Ek (1 <= Ek <= no_of_intersections) – the number of the intersection that is the end of the street. Intersections are represented by integers from 1 to no_of_intersections.
There are no blank lines between consecutive sets of data. Input data are correct.
Output
The result of the program is on standard output. For each input data set the program prints on a single line, starting from the beginning of the line, one integer: the minimum number of paratroopers required to visit all the intersections in the town.
Sample Input
2
4
3
3 4
1 3
2 3
3
3
1 3
1 2
2 3
Sample Output
2
1
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | #include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<queue> using namespace std; const int N=1060; int n1,n2; int _map[N][N]; bool vis[N]; int llink[N]; ?int find(int x) { ????int i; ????for(i=1; i<=n2; i++) { ????????if(_map[x][i]&&!vis[i]) { ????????????vis[i]=true; ????????????if(llink[i]==-1||find(llink[i])) { ????????????????llink[i]=x; ????????????????return true; ????????????} ????????} ????} ????return false; } int mach() { ????int ans=0; ????memset(llink,-1,sizeof(llink)); ????for(int i=1; i<=n1; i++) { ????????memset(vis,false,sizeof(vis)); ????????if(find(i)) { ????????????ans++; ????????} ????} ????return ans; } ?int main() { ????int n,k; ????int T; ????int x,y; ????scanf("%d",&T); ????while(T--) { ????????scanf("%d%d",&n,&k); ????????memset(_map,0,sizeof(_map)); ????????for(int i=1; i<=k; i++) { ????????????scanf("%d%d",&x,&y); ????????????_map[x][y]=1; ????????} ????????n1=n; ????????n2=n; ????????printf("%d\n",n-mach()); ????} ????return 0; } |
4、hdu_2255(最大權匹配的KM算法)可以直接用小康學長的模板
傳說在遙遠的地方有一個非常富裕的村落,有一天,村長決定進行制度改革:重新分配房子。
這可是一件大事,關系到人民的住房問題啊。村里共有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
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | #include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<queue> using namespace std; typedef long long ll; #define inf (int)1e10 #define maxn 350 int edge[maxn][maxn];//鄰接矩陣 int du[maxn],dv[maxn];//可行頂標 int head[maxn];//匹配節點的父節點 bool visu[maxn],visv[maxn];//判斷是否在交錯樹上 int uN;//匹配點的個數 int slack[maxn];//松弛數組 bool dfs(int u) { ????visu[u]=true; ????for(int v=0; v<uN; v++) ????????if(!visv[v]) { ????????????int t=du[u]+dv[v]-edge[u][v]; ????????????if(t==0) { ????????????????visv[v]=true; ????????????????if(head[v]==-1||dfs(head[v])) { ????????????????????head[v]=u; ????????????????????return true; ????????????????} ????????????} else slack[v]=min(slack[v],t); ????????} ????return false; } int KM() { ????memset(head,-1,sizeof(head)); ????memset(du,0,sizeof(du)); ????memset(dv,0,sizeof(dv)); ????for(int u=0; u<uN; u++) ????????for(int v=0; v<uN; v++) ????????????du[u]=max(du[u],edge[u][v]); ????for(int u=0; u<uN; u++) { ????????for(int i=0; i<uN; i++)slack[i]=inf; ????????while(true) { ????????????memset(visu,0,sizeof(visu)); ????????????memset(visv,0,sizeof(visv)); ????????????if(dfs(u))break; ????????????int ex=inf; ????????????for(int v=0; v<uN; v++)if(!visv[v]) ????????????????????ex=min(ex,slack[v]); ????????????for(int i=0; i<uN; i++) { ????????????????if(visu[i])du[i]-=ex; ????????????????if(visv[i])dv[i]+=ex; ????????????????else slack[i]-=ex; ????????????} ????????} ????} ????int ans=0; ????for(int u=0; u<uN; u++) ????????ans+=edge[head[u]][u]; ????return ans; } int main() { //read; ??? while(~scanf("%d",&uN)) { ????????int sum=0; ????????for(int i=0; i<uN; i++) ????????????for(int j=0; j<uN; j++) { ????????????????scanf("%d",&edge[i][j]); ????????????????sum+=edge[i][j]; ????????????} ????????printf("%d\n",KM()); ????} ????return 0; } |
5、hdu_2389 (用Hopcroft-Karp做,用匈牙利算法好像會TLE;)
http://acm.hdu.edu.cn/showproblem.php?pid=2389
You’re giving a party in the garden of your villa by the sea. The party is a huge success, and everyone is here. It’s a warm, sunny evening, and a soothing wind sends fresh, salty air from the sea. The evening is progressing just as you had imagined. It could be the perfect end of a beautiful day.
But nothing ever is perfect. One of your guests works in weather forecasting. He suddenly yells, “I know that breeze! It means its going to rain heavily in just a few minutes!” Your guests all wear their best dresses and really would not like to get wet, hence they stand terrified when hearing the bad news.
You have prepared a few umbrellas which can protect a few of your guests. The umbrellas are small, and since your guests are all slightly snobbish, no guest will share an umbrella with other guests. The umbrellas are spread across your (gigantic) garden, just like your guests. To complicate matters even more, some of your guests can’t run as fast as the others.
Can you help your guests so that as many as possible find an umbrella before it starts to pour?
Given the positions and speeds of all your guests, the positions of the umbrellas, and the time until it starts to rain, find out how many of your guests can at most reach an umbrella. Two guests do not want to share an umbrella, however.
Input
The input starts with a line containing a single integer, the number of test cases.
Each test case starts with a line containing the time t in minutes until it will start to rain (1 <=t <= 5). The next line contains the number of guests m (1 <= m <= 3000), followed by m lines containing x- and y-coordinates as well as the speed si in units per minute (1 <= si <= 3000) of the guest as integers, separated by spaces. After the guests, a single line contains n (1 <= n <= 3000), the number of umbrellas, followed by n lines containing the integer coordinates of each umbrella, separated by a space.
The absolute value of all coordinates is less than 10000.
Output
For each test case, write a line containing “Scenario #i:”, where i is the number of the test case starting at 1. Then, write a single line that contains the number of guests that can at most reach an umbrella before it starts to rain. Terminate every test case with a blank line.
Sample Input
2
1
2
1 0 3
3 0 3
2
4 0
6 0
1
2
1 1 2
3 3 2
2
2 2
4 4
Sample Output
Scenario #1:
2
Scenario #2:
2
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | #include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<queue> #include<vector> using namespace std; #define inf (int)1e10; const int N=50001; int headU[N],headV[N]; int du[N],dv[N]; int uN,vN; vector<int >vec[N]; bool bfs() { ????queue<int>q; ????int dis=inf; ????memset(du,0,sizeof(du)); ????memset(dv,0,sizeof(dv)); ????for(int i=1; i<=uN; i++) ????????if(headU[i]==-1)q.push(i); ????while(!q.empty()) { ????????int u=q.front(); ????????q.pop(); ????????if(du[u]>dis)break; ????????for(int i=0; i<vec[u].size(); i++) ????????????if(!dv[vec[u][i]]) { ????????????????dv[vec[u][i]]=du[u]+1; ????????????????if(headV[vec[u][i]]==-1)dis=dv[vec[u][i]]; ????????????????else { ????????????????????du[headV[vec[u][i]]]=dv[vec[u][i]]+1; ????????????????????q.push(headV[vec[u][i]]); ????????????????} ????????????} ????} ????return dis!=inf; } bool dfs(int u) { ????for(int i=0; i<vec[u].size(); i++) ????????if(dv[vec[u][i]]==du[u]+1) { ????????????dv[vec[u][i]]=0; ????????????if(headV[vec[u][i]]==-1||dfs(headV[vec[u][i]])) { ????????????????headU[u]=vec[u][i]; ????????????????headV[vec[u][i]]=u; ????????????????return 1; ????????????} ????????} ????return 0; } int Hopcroft() { ????memset(headU,-1,sizeof(headU)); ????memset(headV,-1,sizeof(headV)); ????int ans=0; ????while(bfs()) ????????for(int i=1; i<=uN; i++) ????????????if(headU[i]==-1&&dfs(i))ans++; ????return ans; } struct node { ????double x; ????double y; ????double speed; }; node guest[3001]; ?struct node1 { ????double x; ????double y; }; node1 umb[3001]; double dis(node a,node1 b) { ????return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); } ?int main() { ????int T; ????int n,m; ????int x,y; ????double ttime; ????while(~scanf("%d",&T)) { ????????for(int t=1; t<=T; t++) { ????????????for(int i=0; i<N; i++)vec[i].clear(); ????????????scanf("%lf",&ttime); ????????????scanf("%d",&n); ????????????for(int i=1; i<=n; i++) { ????????????????scanf("%lf%lf%lf",&guest[i].x,&guest[i].y,&guest[i].speed); ????????????} ????????????scanf("%d",&m); ????????????for(int i=1; i<=m; i++) { ????????????????scanf("%lf%lf",&umb[i].x,&umb[i].y); ????????????} ????????????for(int i=1; i<=n; i++) { ????????????????for(int j=1; j<=m; j++) { ????????????????????if(dis(guest[i],umb[j])<=guest[i].speed*ttime) { ????????????????????????vec[i].push_back(j); ????????????????????} ????????????????} ????????????} ????????????printf("Scenario #%d:\n",t); ????????????uN=n; ????????????vN=m; ????????????printf("%d\n\n",Hopcroft()); ????????} ????} ????return 0; } |
6、poj_2226(最小頂點覆蓋,構圖比較麻煩)
http://poj.org/problem?id=2226
7.hdu_2444(判斷二分圖+二分匹配)
http://acm.hdu.edu.cn/showproblem.php?pid=2444
8、hdu_2413(二分+二分匹配)
http://acm.hdu.edu.cn/showproblem.php?pid=2413
9、ACdream 1056(判斷是否是二分圖)
http://acdream.info/problem?pid=1056
10、hnu 12939(二分圖最小頂點覆蓋)
http://acm.hnu.cn/online/?action=problem&type=show&id=12939&courseid=279
總結