二分图的判定:
染色法1:假设DFS初始点A涂黑色,与它相邻的点就涂白色。如果搜到某一个点u的相邻点v已经涂色并且与u同色,就不可能是二分图啦~
染色法2:就是给每个点进行标号,标为-1,1如果存在一条边连接的两个点标号相同,那么就是存在一个奇数环…
热身题:
 判断无向图是否有环
用DFS遍历图g,如果访问到已经访问过的顶点,那么有环
| 12
 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
 
 | #include <bits/stdc++.h>#include<algorithm>
 using namespace std;
 #define SIZE 100+5
 
 
 
 
 struct graph{
 int en;
 int vn;
 int map[SIZE][SIZE];
 int vis[SIZE];
 bool huan = false;
 };
 
 
 
 
 
 
 
 void dfs(int u, int prev, graph *g){
 
 for (int v = 0; v < g->vn; ++v){
 
 if (v == prev) continue;
 if ( g->vis[v] != 2 && g->map[u][v] ){
 g->vis[v] = 1;
 dfs(v, u, g);
 }else if(g->vis[v] == 2 && g->map[u][v] ){
 printf("now: %d %d\n",u ,v );
 g->huan = true;
 return ;
 }
 }
 }
 
 void showGraph(const graph *g){
 printf("\t");
 for (int i = 0; i < g->vn; ++i){
 printf("%d ", i);
 }
 printf("\n");
 for (int i = 0; i < g->vn; ++i){
 printf("v:%d\t", i);
 for (int j = 0; j < g->vn; ++j){
 printf("%d ", g->map[i][j]);
 }
 printf("\n");
 }
 }
 
 int main(int argc, char const *argv[]){
 int n;
 graph *g = new graph();
 scanf("%d%d", &g->vn, &g->en);
 memset(g->map, 0, sizeof(int)*g->en*g->vn);
 for (int i = 0; i < g->en; ++i){
 int u, v;
 scanf("%d%d", &u, &v);
 g->map[u][v] = g->map[v][u] = 1;
 }
 showGraph(g);
 
 for (int i = 0; i < g->vn; ++i){
 
 if (g->vis[i] == 0 && g->huan == false){
 g->vis[i] = 2;
 dfs(i, -1, g);
 g->vis[i] = 1;
 }else if(g->huan == true) break;
 }
 if (g->huan) printf("有环\n");
 else printf("无环\n");
 return 0;
 }
 
 | 
1356: Catch
题意:给出一个起始点,一些边,有人从这个起始点开始随意走,问在某一个时候,它是否可以处于任意位置。
思路:思考下,就可以明白,只要是一个联通图,并且存在奇数点形成的环,那么在某一个时候就可以处于任意位置。
 最大匹配数——匈牙利算法
| 12
 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
 
 | #include <bits/stdc++.h>using namespace std;
 
 const int maxn = 150;
 
 int line[maxn][maxn];
 int used[maxn];
 int nxt[maxn];
 
 int m, n, t;
 
 
 
 
 
 
 
 
 bool Find(int x){
 
 for (int i = 1; i <= m; ++i){
 
 if (line[x][i] && !used[i]){
 
 used[i] = 1;
 
 if ( nxt[i]==0 || Find( nxt[i]) ){
 
 nxt[i] = x;
 return true;
 }
 
 }
 }
 return false;
 }
 
 
 
 
 
 
 int match(){
 
 int sum = 0;
 
 for (int i = 1; i <= n; ++i){
 
 memset(used, 0, sizeof(used));
 
 if (Find(i)) sum++;
 
 }
 return sum;
 }
 
 int main(int argc, char const *argv[]){
 int u, v;
 cin >> t;
 
 n = m = 4;
 memset(line, 0, sizeof(line));
 memset(nxt, 0, sizeof(nxt));
 while(t--){
 cin >> u >> v;
 line[u][v] = 1;
 }
 cout << match();
 return 0;
 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 | 
拓展题:无题II HDU2236
二分图:为什么会想到用二分图呢?不同行不同列,仔细想一下,如果某点(x,y),被选中,那么横坐标上x的值不能再选,纵坐标上的y值也不能再选,这相当于二维上有很多值,但是每个值都只能用一次,这就可以想到用二分图匹配求完美匹配了。
二分答案:求n个数的最大最小的差值最小,首先这个差值一定是**0-(maxv-minv)**区间的,然后我们二分差值为外循环,枚举下界为内循环,得到一个区间。做法:修改二分图匹配模板:如果这个二分图的匹配点是在这个区间里的前去匹配,hungry()函数中返回是否为完美匹配,对于每一个二分的差值,只要它能找到一个满足区间条件的完美匹配,就记录一下答案。
Q:首先明白在求什么
A: 首先需要明白:图中每个数值X + 最大差值 <= 最大值
我对上面的理解:
1.最大的差值是个具体的数值δ,最初可以确定的范围在[0, maxv - minv] 之间,因此可以通过二分搜索的方式来找到这个值==>二分搜索
2.那怎么进行二分来缩小这个区间范围呢?Ans:如果符合最大差值的条件(表中所有数都满足 :数值x+差值 <= 最大值),即所有点都能完成匹配,那么证明最大差值在这个区间中---->完美匹配==>匈牙利算法
▲所以算法变成了,不断缩小最大差值可以取值的区间,核心: 判断所有数是否在[p, p+差值]区间内
| 12
 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
 
 | #include <bits/stdc++.h>using namespace std;
 
 const int maxn = 150;
 const int inf=0x3f3f3f3f;
 
 int line[maxn][maxn];
 int used[maxn];
 int nxt[maxn];
 
 int m, n, t, maxv, minv;
 int l, r, mid;
 int p;
 
 
 
 
 
 
 
 
 
 
 
 bool Find(int x){
 for (int i = 1; i <= m; ++i){
 
 
 
 
 
 
 
 
 
 if(p <= line[x][i] && line[x][i]<= p+mid && !used[i]){
 used[i] = 1;
 if ( nxt[i]==0 || Find( nxt[i]) ){
 nxt[i] = x;
 return true;
 }
 }
 }
 return false;
 }
 
 int hungarian(){
 int sum = 0;
 
 memset(nxt, 0, sizeof(nxt));
 for (int i = 1; i <= n; ++i){
 memset(used, 0, sizeof(used));
 if (Find(i)) sum++;
 }
 
 return sum==n?1:0;
 }
 
 int main(int argc, char const *argv[]){
 int size;
 cin >> t;
 while(t--){
 cin >> size;
 m = n = size;
 maxv = -inf;
 minv = inf;
 for (int i = 1; i <= size; ++i){
 for (int j = 1; j <= size; ++j){
 cin >> line[i][j];
 maxv = max(maxv, line[i][j]);
 minv = min(minv, line[i][j]);
 }
 }
 
 l = 0;
 r = maxv - minv;
 int ans = 0;
 
 while( l <= r){
 bool flag = false;
 mid = (l+r) >> 1;
 
 
 for (p = minv; p+mid <= maxv; ++p){
 if (hungarian()){
 
 flag = true;
 break;
 }
 }
 
 if (flag) ans = mid,r = mid-1;
 
 else l = mid+1;
 }
 
 cout << ans << endl;
 }
 return 0;
 }
 
 | 
 最佳匹配——KM算法
解决有权的完美匹配问题,成为最佳匹配,又称为有权完美匹配。最佳匹配同时也是完备匹配
KM算法详解+模板——含图讲解