简述二分图

来源:互联网 发布:java base64encoder 编辑:程序博客网 时间:2024/05/22 14:43

二分图又称作二部图,是图论中的一种特殊模型。 设G=(V,E)是一个无向图,如果顶点V可分割为两个互不相交的子集(A,B),并且图中的每条边(i,j)所关联的两个顶点i和j分别属于这两个不同的顶点集(i in A,j in B),则称图G为一个二分图。

简而言之,就是顶点集V可分割为两个互不相交的子集,并且图中每条边依附的两个顶点都分属于这两个互不相交的子集。
区别二分图,关键是看点集是否能分成两个独立的点集。
1
上图是一个二分图。
2
上图不是一个二分图。

我们可以用染色法判断一个无向图是否是二分图;
从其中一个顶点开始,将跟它邻接的点染成与其不同的颜色,如果邻接的点有相同颜色的,则说明不是二分图,每次用bfs遍历即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
bool BFS(int s,intn) {
    queue<int>p;
    p.push(s);
    col[s]=1;
    while(!p.empty()) {
        intfrom=p.front();
        p.pop();
        for(inti=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]) {
                returnfalse;
            }
        }
    }
    returntrue;
}

匹配:
给定一个二分图G,在G的一个子图M中,M的边集中的任意两条边都不依附于同一个顶点,则称M是一个匹配。
最大匹配:
给定一个二分图G,在G的一个子图M中,M的边集中的任意两条边都不依附于同一个顶点,则称M是一个匹配.
选择这样的边数最大的子集称为图的最大匹配问题(maximal matching problem)

如果一个匹配中,图中的每个顶点都和图中某条边相关联,则称此匹配为完全匹配,也称作完备匹配.
如图所示,图中的最大匹配是4。

3
求最大匹配最常用的算法之一是匈牙利算法。匈牙利算法的核心是寻找增广路径。
**增广路径:
若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,现在查找此网络流的最大流就等同于求此二分图的最大匹配。
4

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>
usingnamespace 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,intnumV) {
    p.clear();
    queue<int> q;
    intchecked[N] = {0};
    q.push(s);
    checked[s] = 1;
    intpre[N];
    memset(pre,-1,sizeof(pre)/sizeof(int));
    intcur;
    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;
    }
    inti = pre[cur];
    p.insert(p.begin(),cur);
    while(i != s) {
        p.insert(p.begin(),i);
        i = pre[i];
    }
    return0;
}
 intmax_fluid(intnumV) {
    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;
    intr_cap = MAX;
    intkey_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];
            }
        }
    }
    intans = 0;
    for(int i = 0; i < numV; i++) {
        if(s != i && fluid[s][i] > 0) {
            ans += fluid[s][i];
        }
    }
    returnans;
}
 doubledistance(doublex1,double y1,double x2,doubley2) {
    returnsqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
}
 intmain() {
    intn,m,S,V;
    doublegophers[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;
            }
        }
        intnumV = 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;
    }
    return0;
}

下面再来讲一下:最大匹配&最小边覆盖&最小路径覆盖&最小顶点覆盖&最大独立集&最大团之间的关系。
【无向图的最大独立数】: 从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>
usingnamespace std; const intN=300; int n1,n2; int _map[N][N];bool vis[N]; int llink[N]; int find(intx) {
    inti;
    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;
                returntrue;
            }
        }
    }
    returnfalse;
} intmach() {
    intans=0;
    memset(llink,-1,sizeof(llink));
    for(inti=1; i<=n1; i++) {
        memset(vis,false,sizeof(vis));
        if(find(i)) {
            ans++;
        }
    }
    returnans;
} intmain() {
    intn,m;
    intnum;
    intb;
    while(~scanf("%d%d",&n,&m)) {
        memset(_map,0,sizeof(_map));
        for(inti=1; i<=n; i++) {
            scanf("%d",&num);
            for(intj=0; j<num; j++) {
                scanf("%d",&b);
                _map[i][b]=1;
            }
        }
        n1=n;
        n2=m;
        printf("%d\n",mach());
    }
    return0;
}

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>
usingnamespace std; const intN=1006; int n1,n2; int _map[N][N];bool vis[N]; int llink[N]; int find(intx) {
    inti;
    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;
                returntrue;
            }
        }
    }
    returnfalse;
} intmach() {
    intans=0;
    memset(llink,-1,sizeof(llink));
    for(inti=0; i<n1; i++) {
        memset(vis,false,sizeof(vis));
        if(find(i)) {
            ans++;
        }
    }
    returnans;
} intmain() {
    intn,m;
    intk;
    intjob,x,y;
    while(~scanf("%d",&n)) {
        if(n==0) {
            break;
        }
        scanf("%d%d",&m,&k);
        memset(_map,0,sizeof(_map));
  
        for(inti=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;
        intans1=mach();
        printf("%d\n",ans1);;
    }
    return0;
}

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>
usingnamespace std; const intN=1060; int n1,n2; int _map[N][N];bool vis[N]; int llink[N];
 intfind(int x) {
    inti;
    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;
                returntrue;
            }
        }
    }
    returnfalse;
} intmach() {
    intans=0;
    memset(llink,-1,sizeof(llink));
    for(inti=1; i<=n1; i++) {
        memset(vis,false,sizeof(vis));
        if(find(i)) {
            ans++;
        }
    }
    returnans;
}
 intmain() {
    intn,k;
    intT;
    intx,y;
    scanf("%d",&T);
    while(T--) {
        scanf("%d%d",&n,&k);
        memset(_map,0,sizeof(_map));
        for(inti=1; i<=k; i++) {
            scanf("%d%d",&x,&y);
            _map[x][y]=1;
        }
        n1=n;
        n2=n;
        printf("%d\n",n-mach());
    }
    return0;
}

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>
usingnamespace std; typedef longlong ll; #define inf (int)1e10
#define maxn 350
int edge[maxn][maxn];//邻接矩阵
int du[maxn],dv[maxn];//可行顶标
int head[maxn];//匹配节点的父节点
boolvisu[maxn],visv[maxn];//判断是否在交错树上
int uN;//匹配点的个数
int slack[maxn];//松弛数组
booldfs(int u) {
    visu[u]=true;
    for(intv=0; v<uN; v++)
        if(!visv[v]) {
            intt=du[u]+dv[v]-edge[u][v];
            if(t==0) {
                visv[v]=true;
                if(head[v]==-1||dfs(head[v])) {
                    head[v]=u;
                    returntrue;
                }
            }else slack[v]=min(slack[v],t);
        }
    returnfalse;
} intKM() {
    memset(head,-1,sizeof(head));
    memset(du,0,sizeof(du));
    memset(dv,0,sizeof(dv));
    for(intu=0; u<uN; u++)
        for(intv=0; v<uN; v++)
            du[u]=max(du[u],edge[u][v]);
    for(intu=0; u<uN; u++) {
        for(inti=0; i<uN; i++)slack[i]=inf;
        while(true) {
            memset(visu,0,sizeof(visu));
            memset(visv,0,sizeof(visv));
            if(dfs(u))break;
            intex=inf;
            for(intv=0; v<uN; v++)if(!visv[v])
                    ex=min(ex,slack[v]);
            for(inti=0; i<uN; i++) {
                if(visu[i])du[i]-=ex;
                if(visv[i])dv[i]+=ex;
                elseslack[i]-=ex;
            }
        }
    }
    intans=0;
    for(intu=0; u<uN; u++)
        ans+=edge[head[u]][u];
    returnans;
} intmain() { //read;     while(~scanf("%d",&uN)) {
        intsum=0;
        for(inti=0; i<uN; i++)
            for(intj=0; j<uN; j++) {
                scanf("%d",&edge[i][j]);
                sum+=edge[i][j];
            }
        printf("%d\n",KM());
    }
    return0;
}

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>
usingnamespace std; #define inf (int)1e10;
constint N=50001; int headU[N],headV[N];int du[N],dv[N];int uN,vN;
vector<int>vec[N]; boolbfs() {
    queue<int>q;
    intdis=inf;
    memset(du,0,sizeof(du));
    memset(dv,0,sizeof(dv));
    for(inti=1; i<=uN; i++)
        if(headU[i]==-1)q.push(i);
    while(!q.empty()) {
        intu=q.front();
        q.pop();
        if(du[u]>dis)break;
        for(inti=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]]);
                }
            }
    }
    returndis!=inf;
} booldfs(int u) {
    for(inti=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;
                return1;
            }
        }
    return0;
} intHopcroft() {
    memset(headU,-1,sizeof(headU));
    memset(headV,-1,sizeof(headV));
    intans=0;
    while(bfs())
        for(inti=1; i<=uN; i++)
            if(headU[i]==-1&&dfs(i))ans++;
    returnans;
} structnode {
    doublex;
    doubley;
    doublespeed;
};
node guest[3001];
 structnode1 {
    doublex;
    doubley;
};
node1 umb[3001]; double dis(node a,node1 b) {
    returnsqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
 intmain() {
    intT;
    intn,m;
    intx,y;
    doublettime;
    while(~scanf("%d",&T)) {
        for(intt=1; t<=T; t++) {
            for(inti=0; i<N; i++)vec[i].clear();
            scanf("%lf",&ttime);
            scanf("%d",&n);
            for(inti=1; i<=n; i++) {
                scanf("%lf%lf%lf",&guest[i].x,&guest[i].y,&guest[i].speed);
            }
            scanf("%d",&m);
            for(inti=1; i<=m; i++) {
                scanf("%lf%lf",&umb[i].x,&umb[i].y);
            }
            for(inti=1; i<=n; i++) {
                for(intj=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());
        }
    }
    return0;
}

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

0 0
原创粉丝点击