二分图的判定:
染色法1:假设DFS初始点A涂黑色,与它相邻的点就涂白色。如果搜到某一个点u的相邻点v已经涂色并且与u同色,就不可能是二分图啦~
染色法2:就是给每个点进行标号,标为-1,1如果存在一条边连接的两个点标号相同,那么就是存在一个奇数环…
热身题:
判断无向图是否有环
用DFS遍历图g,如果访问到已经访问过的顶点,那么有环
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
| #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
题意:给出一个起始点,一些边,有人从这个起始点开始随意走,问在某一个时候,它是否可以处于任意位置。
思路:思考下,就可以明白,只要是一个联通图,并且存在奇数点形成的环,那么在某一个时候就可以处于任意位置。
最大匹配数——匈牙利算法
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
| #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+差值]区间内
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
| #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算法详解+模板——含图讲解