定义
如果一个图任意边的两个端点都分属于两个集合,则称图
为一个为二分图

匹配
一个匹配即一个包含若干条边的集合,且其中任意两条边没有公共端点。
如图,红边即为一组匹配

最大匹配
在 G 的一个子图 M 中,M 的边集中的任意两条边都不依附于同一个顶点,则称 M 是一个匹配。
选择这样的边数最大的子集称为图的最大匹配问题,最大匹配的边数称为最大匹配数。如果一个匹配中,图中的每个顶点都和图中某条边相关联,则称此匹配为完全匹配,也称作完备匹配。如果在左右两边加上源汇点后,图G等价于一个网络流,最大匹配问题可以转为最大流的问题。解决此问的匈牙利算法的本质就是寻找最大流的增广路径
染色法判定二分图
我们将相邻的两个点标记为不同的颜色,即每条边的两个顶点为不同的颜色,如果有冲突,则这个图不为二分图(实现多用D/BFS)
#include<bits/stdc++.h>
#define pii pair<int, int>
using namespace std;
const int N = 2e5 + 5;
int n, m;//点数和边数
int g[N];
int color[N];//1为红色,2为蓝色
signed main(){
cin >> n >> m;
for(int u, v, i = 1; i <= m; i++){
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
//BFS
queue<int> q;
q.push(1);
color[1] = 1;//先将第一个点标为红色
while(q.size()){
int u = q.front();
for(int v : g[u]){
if(color[v] == color[u]){//颜色冲突,不是二分图
puts("NO");
return 0;
}
color[v] = 3 - color[u];//标记颜色
q.push(v);
}
}
puts("YES");
return 0;
}
匈牙利算法找最大匹配
#include<bits/stdc++.h>
using namespace std;
const int N = 505;
vector<int> g[N];
int n, m, k, ans;
bool st[N];//判断这个点有没有被匹配
int match[N];//储存这个点匹配的另一个点
bool find(int u){
for(int v : g[u]){
if(!st[v]){//如果还没有被匹配
st[v] = 1;
if(!match[v]/*还没有匹配*/|| find(match[v])/*已经和这个点匹配的另一个点可以换一个点匹配*/){
match[v] = u;/*匹配*/
return 1;
}
}
}
return 0;
}
int main(){
cin >> n >> m >> k;
for(int i = 0, u, v; i < k; i++){
cin >> u >> v;
g[u].push_back(v);
}
for(int i = 1; i <= n; i++){
memset(st, 0, sizeof st);
if(find(i)) ans++;//如果能匹配上
}
cout << ans;
return 0;
}
洛谷 P1894 [USACO4.2] 完美的牛栏The Perfect Stall
//同上
#include<bits/stdc++.h>
using namespace std;
const int N = 205;
int n, m, ans;
vector<int> g[N];
bool st[N];
int match[N];
bool find(int u){
for(int v : g[u]){
if(!st[v]){
st[v] = 1;
if(!match[v] || find(match[v])){
match[v] = u;
return 1;
}
}
}
return 0;
}
int main(){
cin >> n >> m;
for(int i = 1, x, y; i <= n; i++){
cin >> x;
for(int j = 1; j <= x; j++){
cin >> y;
g[i].push_back(y);
}
}
for(int i = 1; i <= n; i++){
memset(st, 0, sizeof st);
if(find(i)) ans++;
}
cout << ans;
return 0;
}
先二分枚举答案,然后染色法判定,最后选出最优答案
#include<bits/stdc++.h>
#define pii pair<int, int>
using namespace std;
const int N = 2e4 + 5;
int n, m;
vector<pii> g[N];
int color[N];
bool dfs(int u, int c, int limit){
color[u] = c;
for(pii t : g[u]){
int v = t.first, w = t.second;
if(w <= limit) continue;
if(color[v]){
if(color[v] == c) return 0;
}
else if(!dfs(v, 3 - c, limit)) return 0;
}
return 1;
}
bool check(int mid){
memset(color, 0, sizeof color);
for(int i = 1; i <= n; i++)
if(!color[i]){
if(!dfs(i, 1, mid))
return 0;
}
return 1;
}
int main(){
cin >> n >> m;
for(int i = 1, u, v, w; i <= m; i++){
cin >> u >> v >> w;
g[u].push_back({v, w});
g[v].push_back({u, w});
}
int l = 0, r = 1e9;
while(l < r){
int mid = l + r >> 1;
if(check(mid)) r = mid;
else l = mid + 1;
}
cout << l;
return 0;
}
将行和列是为两个点,用行去匹配列,特判那些禁用的点
#include<bits/stdc++.h>
#define pii pair<int, int>
using namespace std;
const int N = 205;
int n, m, t, ans;
int a[N][N];
bool st[N];
int match[N];
bool find(int x){
for(int i = 1; i <= m; i++){
if(!st[i] && !a[x][i]){
st[i] = 1;
if(!match[i] || find(match[i])){
match[i] = x;
return 1;
}
}
}
return 0;
}
int main(){
cin >> n >> m >> t;
for(int x, y, i = 0; i < t; i++){
cin >> x >> y;
a[x][y] = 1;
}
for(int i = 1; i <= n; i++){
memset(st, 0, sizeof st);
if(find(i)) ans++;
}
cout << ans;
return 0;
}
枚举每个点,用这个点去匹配其他骑士能到的八个点。
#include<bits/stdc++.h>
#define pii pair<int, int>
using namespace std;
const int N = 105;
int n, m, t, ans;
int a[N][N];
bool st[N][N];
pii match[N][N];
int dir[8][2] = {1, 2, 2, 1, -1, -2, -2, -1, 1, -2, -1, 2, 2, -1, -2, 1};
bool find(int x, int y){
for(int i = 0; i < 8; i++){
int xx = x + dir[i][0];
int yy = y + dir[i][1];
if(xx < 1 || yy < 1 || xx > n || yy > m) continue;
if(a[xx][yy] || st[xx][yy]) continue;
st[xx][yy] = 1;
pii t = match[xx][yy];
if(t.first == 0 || find(t.first, t.second)){
match[xx][yy] = {x, y};
return 1;
}
}
return 0;
}
int main(){
cin >> n >> m >> t;
for(int x, y, i = 0; i < t; i++){
cin >> x >> y;
a[x][y] = 1;
}
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
if(a[i][j] || (i + j) % 2) continue;
memset(st, 0, sizeof st);
if(find(i, j)) ans++;
}
}
cout << n * m - ans - t;
return 0;
}
传递闭包+匈牙利算法+有向图
#include<bits/stdc++.h>
#define pii pair<int, int>
using namespace std;
const int N = 205;
int n, m, ans;
bool st[N];
int match[N];
int g[N][N];
bool find(int u){
for(int v = 1; v <= n; v++){
if(!g[u][v]) continue;
if(!st[v]){
st[v] = 1;
if(!match[v] || find(match[v])){
match[v] = u;
return 1;
}
}
}
return 0;
}
int main(){
cin >> n >> m;
for(int i = 1, u, v; i <= m; i++){
cin >> u >> v;
g[u][v] = 1;
}
for(int k = 1; k <= n; k++)
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
g[i][j] |= g[i][k] & g[k][j];
for(int i = 1; i <= n; i++){
memset(st, 0, sizeof st);
if(find(i)) ans++;
}
cout << n - ans;
return 0;
}
&spm=1001.2101.3001.5002&articleId=147687327&d=1&t=3&u=7a265c2a1c314fdb8f6ec163759f5bf6)
975

被折叠的 条评论
为什么被折叠?



