版权声明:本博客主要用于记录思考过程,方便复习,欢迎留言交流讨论~ https://blog.csdn.net/Floraqiu/article/details/81908886
判断是否为二分图(染色法)
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <list>
#define clr(x, y) memset(x, y, sizeof(x))
#define MOD 1000000000+7
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 505;
int col[maxn];
bool bfs(int s)
{
queue<int> q;
q.push(s);
color[s] = 1;
while(!q.empty())
{
int from = q.front();
q.pop();
for(int i = 1; i <= n; i++)
{
if(mapp[from][i] && color[i] == -1)
{
q.push(i);
color[i] = !color[from];//染成不同的颜色
}
if(mapp[from][i] && color[from] == color[i])//颜色有相同,则不是二分图
return false;
}
}
return true;
}
bool flag = true;
for(i = 1; i <= n; i++)
if(color[i] == -1 && !bfs(i)) //遍历各个连通分支
{
flag = false;//flag为假说明不是二分图
break;
}
求二分图最大匹配的匈牙利算法
以 [过山车 HDU - 2063 ]为例
1、邻接矩阵
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <list>
#define clr(x, y) memset(x, y, sizeof(x))
#define MOD 1000000000+7
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 505;
int k, m, n, mapp[maxn][maxn];
int link[maxn], vis[maxn];
bool found(int u)
{
for(int i = 1; i <= n; i++)
{
if(!vis[i] && mapp[u][i])
{
vis[i] = 1;
if(link[i] == -1 || found(link[i]))
{
link[i] = u;
return true;
}
}
}
return false;
}
int sol()
{
int num = 0;
clr(link, -1);
for(int i = 1; i <= m; i++)
{
clr(vis, 0);
if(found(i)) num++;
}
return num;
}
int main()
{
while(~scanf("%d%", &k) && k)
{
scanf("%d%d", &m, &n);
clr(mapp, 0);
for(int i = 1; i <= k; i++)
{
int x, y;
scanf("%d%d", &x, &y);
mapp[x][y] = 1;
}
printf("%d\n", sol());
}
return 0;
}
2、邻接表
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <list>
#define clr(x, y) memset(x, y, sizeof(x))
#define MOD 1000000000+7
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 505;
int k, m, n;
struct maxmatch
{
struct node
{
int to, next;
} edge[maxn * maxn];
int tot, head[maxn];
int link[maxn], vis[maxn];
void init()
{
tot = 0;
clr(head, -1);
clr(link, 0);
}
void addedge(int u, int v)
{
edge[tot].to = v;
edge[tot].next = head[u];
head[u] = tot++;
}
bool found(int u)
{
for(int i = head[u]; i != -1; i = edge[i].next)
{
int v = edge[i].to;
if(!vis[v])
{
vis[v] = 1;
if(!link[v] || found(link[v]))
{
link[v] = u;
return true;
}
}
}
return false;
}
int match()
{
int num = 0;
for(int i = 1; i <= m; i++)
{
clr(vis, 0);
if(found(i)) num++;
}
return num;
}
};
int main()
{
maxmatch mp;
while(~scanf("%d%", &k) && k)
{
scanf("%d%d", &m, &n);
mp.init();
for(int i = 1; i <= k; i++)
{
int x, y;
scanf("%d%d", &x, &y);
mp.addedge(x, y);
}
printf("%d\n", mp.match());
}
return 0;
}
二分图的最佳匹配 KM算法
朴素版 O(n^4)
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <list>
#define clr(x, y) memset(x, y, sizeof(x))
#define MOD 1000000000+7
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 505;
bool findpath(int x)
{
visx[x] = true;
for(int y = 1; y <= ny; ++y)
{
if(!vis[y] && lx[x] + ly[y] == w[x][y])
{
vis[y] = true;
if(match[y] == -1 || findpath(match[y]))
{
match[y] = x;
return true;
}
}
}
return false;
}
void KM()
{
for(int x = 1; x <= nx; ++x)
{
while(true)
{
clr(visx, false);//访问过X中的标记
clr(visy, false);//访问过y中的标记
if(findpath(x)) break;//找到了增广路,跳出继续寻找下一个
else //更新可行顶标
{
int delta = INF;
for(int i = 1; i <= nx; ++i)
{
if(visx[i])//i在交错路中
{
for(int j = 1; j <= ny; ++j)
{
if(!vis[j])//j不在交错路中
delta = min(delta, lx[i] + ly[j] - w[i][j]);
}
}
}
for(int i = 1; i <= nx; ++i)
if(visx[i]) lx[i] -= delta;
for(int j = 1; j <= ny; ++j)
if(visy[j]) ly[j] += delta;
}
}
}
}
int solve()
{
clr(match, -1);
clr(ly, 0);
for(int i = 1; i <= nx; ++i)
{
lx[i] = -INF;
for(int j = 1; j <= ny; ++j)
lx[i] = max(lx[i], w[i][j]);
}
KM();
int ans = 0;
for(int i = 0; i < ny; ++i)
if(match[i] != -1)
ans += G[match[i]][i];
return ans;
}
优化版 O(n^3)
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <math.h>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <list>
#define clr(x, y) memset(x, y, sizeof(x))
#define MOD 1000000000+7
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 505;
/*
实际上,O(n^4)的KM算法表现不俗,使用O(n^3)并不会很大的提高KM的运行效率
需要在O(1)的时间找到任意一条边,使用邻接矩阵存储更为方便
*/
int match[maxn], lx[maxn], ly[maxn], slack[maxn];
int G[maxn][maxn];
bool visx[maxn], visy[maxn];
int n, nx, ny, ans;
bool findpath(int x)
{
int tempDelta;
visx[x] = true;
for(int y = 0; y < ny; ++y)//对每一条边都计算一遍delta
{
if(visy[y]) continue;
tempDelta = lx[x] + ly[y] - G[x][y];
if(tempDelta == 0)//说明(x,y)在相等子图里
{
visy[y] = true;
if(match[y] == -1 || findpath(match[y]))
{
match[y] = x;
return true;
}
}
else slack[y] = min(slack[y], tempDelta);//(x,y)不在相等子图中且x在交错树中,y不在交错树中
}
return false;
}
void KM()
{
for(int x = 0; x < nx; ++x)
{
clr(slack, INF);//每次换新的x结点都要初始化slack
while(true)
{
//每次findpath()都要更新,所以必须清理vis
clr(visx, false);
clr(visy, false);
if(findpath(x)) break;
else
{
int delta = INF;
for(int j = 0; j < ny; ++j)//之前dfs时,x一定在交错树中,dfs里对这些x都进行了松弛,这里直接找出最小的更新即可
if(!visy[j]) delta = min(delta, slack[j]);
for(int i = 0; i < nx; ++i)
if(visx[i]) lx[i] -= delta;
for(int j = 0; j < ny; ++j)
if(visy[j]) ly[j] += delta;
else slack[j] -= delta;
//修改顶标后,要把所有的slack值都减去delta
//这是因为所有在交错树中的lx[i] 减小了delta, 而不在交错树中的ly[j]没有变,w[i][j]也没变
//slack[j] = min(lx[i] + ly[j] -w[i][j])
}
}
}
}
int solve()
{
clr(match, -1);
clr(ly, 0);
for(int i = 0; i < nx; ++i)
{
lx[i] = -INF;
for(int j = 0; j < ny; ++j)
lx[i] = max(lx[i], G[i][j]);
}
KM();
int ans = 0;
for(int i = 0; i < ny; ++i)
if(match[i] != -1)
ans += G[match[i]][i];
return ans;
}