团
团即一个点集,集合中任两个结点相邻。或者说是导出的子图是完全图的点集。极大团(maximal clique):本身为团,再加入任何点都不是。最大团(maximum clique):点最多的团。团数(clique number):最大团的点数。根据一个匹配是最大匹配当且仅当没有增广路,求最大匹配就是找增广轨,直到找不到增广轨,就找到了最大匹配。遍历每个点,查找增广路,若找到增广路,则修改匹配集和匹配数,否则,终止算法,返回最大匹配数。
#define maxn 10//表示x集合和y集合中顶点的最大个数!
int nx,ny;//x集合和y集合中顶点的个数
int edge[maxn][maxn];//edge[i][j]为1表示ij可以匹配
int cx[maxn],cy[maxn];//用来记录x集合中匹配的y元素是哪个!
int visited[maxn];//用来记录该顶点是否被访问过!
int path(int u)
{
int v;
for(v=0;v
BFS#define maxn 10;
int pred[maxn];
int cx,cy;
int nx,ny;
int visited[maxn];
int edge[maxn][maxn];
int queue[maxn];// 用来模拟队列
int maxmatch()
{
int i,j,y;
int cur,tail;
int res=0;
memset(cx,0xff,sizeof(cx));
memset(cy,0xff,sizeof(cx));
for(i=0;i-1)
{
cx[cy[pred[y]]]=y;
cy[y]=cy[pred[y]];
y=pred[y];
}
cy[y]=i;
cx[i]=y;
res++;
}
return res;
}
#include
#include
#include
using namespace std;
const int MAXN = 310;
const int INF = 1 << 28;
bool flag;
int p,n;
int Mx[MAXN], My[MAXN], Nx, Ny;
int dx[MAXN], dy[MAXN], dis;
bool vst[MAXN],g[110][310];
bool searchP(void) //BFS
{
queue Q;
dis = INF;
memset(dx, -1, sizeof(dx));
memset(dy, -1, sizeof(dy));
for (int i = 1; i <= Nx; i++)
if (Mx[i] == -1){
Q.push(i); dx[i] = 0;
}
while (!Q.empty()) {
int u = Q.front(); Q.pop();
if (dx[u] > dis) break; //说明该增广路径长度大于dis还没有结束,等待下一次BFS在扩充
for (int v = 1; v <= Ny; v++)
if (g[u][v] && dy[v] == -1) { //v是未匹配点
dy[v] = dx[u]+1;
if (My[v] == -1) dis = dy[v]; //得到本次BFS的最大遍历层次
else{
dx[My[v]] = dy[v]+1; //v是匹配点,继续延伸
Q.push(My[v]);
}
}
}
return dis != INF;
}
bool DFS(int u){
for (int v = 1; v <= Ny; v++)
if (!vst[v] && g[u][v] && dy[v] == dx[u]+1) {
vst[v] = 1;
if (My[v] != -1 && dy[v] == dis) continue; //层次(也就是增广路径的长度)大于本次查找的dis,是searchP被break的情况,也就是还不确定是否是增广路径,只有等再次调用searchP()在判断。
if (My[v] == -1 || DFS(My[v])) { //是增广路径,更新匹配集
My[v] = u; Mx[u] = v;
return 1;
}
}
return 0;
}
int MaxMatch(void){
int res = 0;
memset(Mx, -1, sizeof(Mx));
memset(My, -1, sizeof(My));
while (searchP()) {
memset(vst, 0, sizeof(vst));
for (int i = 1; i <= Nx; i++)
if (Mx[i] == -1 && DFS(i)) res++; //查找到一个增广路径,匹配数res++
}
return res;
}
/**********************************************************************/
int main()
{
int i,j,k,t,v,cnt;
scanf("%d",&t);
while (t--)
{
scanf("%d %d", &p, &n);
for (i = 1; i <= p; i++)
for (j = 1; j <= n; j++)
g[i][j] = false;
flag = true;
for (i = 1; i <= p; i++)
{
scanf("%d",&k);
if (k == 0)
flag = false;
while (k--)
{
scanf("%d",&v);
g[i][v] = true;
}
}
Nx = p; Ny = n;
if (flag)
{
cnt = MaxMatch();
if (cnt == p)
printf("YES\n");
else printf("NO\n");
}
else printf("NO\n");
}
return 0;
}
╝②#include
#include
#define CAP 50010
int n, m;
int mx[CAP], my[CAP], dis[CAP], que[CAP];
bool used[CAP];
struct Node {
int id;
struct Node *next;
}adj[CAP];
bool BFS()
{
int front, rear;
int i, j;
front = rear = 0;
for (i=1; i<=n; i++) {
if (mx[i] < 0) {
dis[i] = 0;
que[rear++] = i;
used[i] = true;
}else {
used[i] = false;
}
}
bool suc = false;
while (front < rear) {
int u = que[front++];
struct Node *p = &(adj[u]);
while (p->next) {
int v = p->next->id;
if (my[v] < 0) suc = true;
else if (!used[my[v]]) {
dis[my[v]] = dis[u]+1;
used[my[v]] = true;
que[rear++] = my[v];
}
p = p->next;
}
}
return suc;
}
bool DFS(int u)
{
struct Node *p = &(adj[u]);
while (p->next) {
int v = p->next->id;
if (my[v] < 0
|| dis[my[v]] == dis[u]+1 && DFS(my[v])) {
my[v] = u;
mx[u] = v;
dis[u] = -1;
return true;
}
p = p->next;
}
return false;
}
int main()
{
int i, j, P;
int a, b;
struct Node *p;
while (scanf("%d%d%d", &n, &m, &P) != EOF) {
for (i=1; i<=n; i++)
adj[i].next = NULL;
for (i=0; iid = b;
p->next = adj[a].next;
adj[a].next = p;
}
memset(mx, -1, sizeof(mx));
memset(my, -1, sizeof(my));
int match = 0;
while (BFS()) {
for (i=1; i<=n; i++) {
if (mx[i] < 0 && DFS(i))
match++;
}
}
printf("%d\n", match);
}
return 0;
}
定理1:最小覆盖数 = 最大匹配数
#include
#include
#include
#include
#include
#define maxn 10000
using namespace std;
vectornode[maxn];
int mm[maxn];
int visit[maxn];
int n,m;
void init()
{
cin>>n>>m;//输入点数,边数
for(int i=0;i<=n;i++)
node[i].clear();
for(int i=0;i>a>>b;
node[a].push_back(b);//建边
}
}
int dfs(int fa)
{
for(int i=0;i>test;
while(test--)
{
init();
solve();
}
return 0;
}
╝⑧二分图完备匹配判断实现可参考http://www.cnblogs.com/moonbay/archive/2012/08/16/2642042.html。
二分图的带权匹配就是求出一个匹配集合,使得集合中边的权值之和最大或最小。Kuhn-Munkers算法是通过给每个顶点一个标号(叫做顶标)来把求最大权匹配的问题转化为求完备匹配的问题的。设顶点Xi的顶标为A[i],顶点Yi的顶标为B[i],顶点Xi与Yj之间的边权为w[i,j]。在算法执行过程中的任一时刻,对于任一条边(i,j), A[i]+B[j]>=w[i,j]始终成立。
若由二分图中所有满足A[i]+B[j]=w[i,j]的边(i,j)构成的子图(称做相等子图)有完备匹配,那么这个完备匹配就是二分图的最大权匹配。
#include
#include
#include
using namespace std;
const int N = 128;
const int INF = 1 << 28;
class Graph {
private:
bool xckd[N], yckd[N];
int n, edge[N][N], xmate[N], ymate[N];
int lx[N], ly[N], slack[N], prev[N];
queue Q;
bool bfs();
void agument(int);
public:
bool make();
int KMMatch();
};
bool Graph::make() {
int house[N], child[N], h, w, cn = 0;
char line[N];
scanf("%d %d", &h, &w);
if(w == 0) return false;
scanf("\n"); n = 0;
for(int i = 0; i < h; i++) {
gets(line);
for(int j = 0; line[j] != 0; j++) {
if(line[j] == 'H') house[n++] = i * N + j;
if(line[j] == 'm') child[cn++] = i * N + j;
}
}
for(int i = 0; i < n; i++) {
int cr = child[i] / N, cc = child[i] % N;
for(int j = 0; j < n; j++) {
int hr = house[j] / N, hc = house[j] % N;
edge[i][j] = -abs(cr-hr) - abs(cc-hc);
}
}
return true;
}
bool Graph::bfs() {
while(!Q.empty()) {
int p = Q.front(), u = p>>1; Q.pop();
if(p&1) {
if(ymate[u] == -1) { agument(u); return true; }
else { xckd[ymate[u]] = true; Q.push(ymate[u]<<1); }
} else {
for(int i = 0; i < n; i++)
if(yckd[i]) continue;
else if(lx[u]+ly[i] != edge[u][i]) {
int ex = lx[u]+ly[i]-edge[u][i];
if(slack[i] > ex) { slack[i] = ex; prev[i] = u; }
} else {
yckd[i] = true; prev[i] = u;
Q.push((i<<1)|1);
}
}
}
return false;
}
void Graph::agument(int u) {
while(u != -1) {
int pv = xmate[prev[u]];
ymate[u] = prev[u]; xmate[prev[u]] = u;
u = pv;
}
}
int Graph::KMMatch() {
memset(ly, 0, sizeof(ly));
for(int i = 0; i < n; i++) {
lx[i] = -INF;
for(int j = 0; j < n; j++) lx[i] >?= edge[i][j];
}
memset(xmate, -1, sizeof(xmate)); memset(ymate, -1, sizeof(ymate));
bool agu = true;
for(int mn = 0; mn < n; mn++) {
if(agu) {
memset(xckd, false, sizeof(xckd));
memset(yckd, false, sizeof(yckd));
for(int i = 0; i < n; i++) slack[i] = INF;
while(!Q.empty()) Q.pop();
xckd[mn] = true; Q.push(mn<<1);
}
if(bfs()) { agu = true; continue; }
int ex = INF; mn--; agu = false;
for(int i = 0; i < n; i++)
if(!yckd[i]) ex = slack[i];
for(int i = 0; i < n; i++) {
if(xckd[i]) lx[i] -= ex;
if(yckd[i]) ly[i] += ex;
slack[i] -= ex;
}
for(int i = 0; i < n; i++)
if(!yckd[i] && slack[i] == 0) { yckd[i] = true; Q.push((i<<1)|1); }
}
int cost = 0;
for(int i = 0; i < n; i++) cost += edge[i][xmate[i]];
return cost;
}
int main()
{
Graph g;
while(g.make()) printf("%d\n", -g.KMMatch());
return 0;
}
Kuhn-Munkers算法的几种变形应用