二分图算法

#PermanentNotes/algorithm

匈牙利算法

推荐视频
D25 二分图最大匹配 匈牙利算法——信息学竞赛算法_哔哩哔哩_bilibili

思想

主要是围着"腾空间"来实现
当我们从A集合,B集合中寻找能够配对的个数时,我们首先枚举每一个集合A,然后,按照下方步骤:
假设我们遍历A的第Ai个
1.遍历Ai配对的Bi
2.此时,如果Bi已经被访问过,我们就返回1否则,就标记
3.标记之后,我们判断此时Bi是否已经配对,如果没有配对,那么就让Ai与Bi配对,否则,我们将Bi配对的Aii从1开始,判断Aii能否将此时的Bi让出来,让Aii与其他能够配对的B配对
4,如果配对成功,返回true否则返回1
5.返回false

#include
#include
#include
#include
#include

using namespace std;

const int Z = 1e5;

struct edge {
	int v;
	int nxt;
}e[Z];

int tot = 1;
int h[Z];
void add_edge(int u, int v) {
	tot++;
	e[tot].v = v;
	e[tot].nxt = h[u];
	h[u] = tot;
}
int n, m;

int match[Z];
int vis[Z];

bool dfs(int u) {
	for (int i = h[u]; i; i = e[i].nxt) {
		int v = e[i].v;
		if (vis[v])continue;
		vis[v] = 1;
		if (match[v] == 0 || dfs(match[v])) {
			match[v] = u;
			return true;
		}
	}
	return false;
}

int main(void) {
	while (cin >> n >> m) {
		for (int i = 1; i <= n; i++) {
				int a;
			cin >> a;
			for (int j = 0; j < a; j++) {
				int v;
				cin >> v;
				add_edge(i, v);
			}
		}
		int ans = 0;
		for (int i = 1; i <= n; i++) {
			memset(vis, 0, sizeof(vis));
			if (dfs(i) == true)ans++;
		}
		cout << ans << endl;
	}
	return 0;
}

最大独立集

推荐视频
1466 – Girls and Boys

思想

显然,这题是需要用到二分图的,但是最终我们的答案是二分图减去匹配数/2,原因:首先,在我们匹配关系的时候,我们是将一个点拆分成两个点,假设此时有7个人进行匹配,那么我们匹配的时候相当于2n个,左边是n个男生,右边是n个女生,所以,最终匹配的个数应该是n-匹配数/2

代码

#include
#include
#include
#include
#include
#include

using namespace std;

const int z = 1e5;

struct edge {
	int v;
	int nxt;
}e[z];
int tot = 1;
int h[z];
void add_edge(int u, int v) {
	tot++;
	e[tot].v = v;
	e[tot].nxt = h[u];
	h[u] = tot;
}
int n;
int match[z];
int vis[z];
bool dfs_match(int u) {
	for (int i = h[u]; i!=-1; i = e[i].nxt) {
		int v = e[i].v;
		if (vis[v] == 0) {
			vis[v] = 1;
			if (match[v] == -1 || dfs_match(match[v])) {
				match[v] = u;
				return true;
			}
		}
	}
	return false;
}

int main(void) {
	while (cin>>n) {
		memset(h, -1, sizeof(h));
		memset(match, -1, sizeof(match));
		tot = 1;
		for (int i = 1; i <= n; i++) {
			//0: (3) 4 5 6
			int a, b;
			scanf("%d: (%d)", &a, &b);
			while (b--) {
				int m;
				scanf("%d",&m);
				add_edge(a, m);
			}
		}
		int ans = 0;
		for (int i = 0; i < n; i++) {
			memset(vis, 0, sizeof(vis));
			ans += dfs_match(i);
		}

		cout << n - ans/2 << endl;
	}
	return 0;
}

二分图最大权完美匹配KM算法

推荐视频
D27 二分图最大权完美匹配 KM算法_哔哩哔哩_bilibili

基本概念

可行顶标:我们将每个结点分配一个权重 l a [ x ] , l b [ y ] la[x],lb[y] la[x],lb[y] ,对于所有边(x,y)满足 l a [ x ] + l b [ y ] > = w ( x , y ) . la[x]+lb[y]>=w(x,y). la[x]+lb[y]>=w(x,y).
相等子图: l a [ x ] + l b [ y ] = w ( x , y ) la[x]+lb[y]=w(x,y) la[x]+lb[y]=w(x,y)
定理:如果相同子图存在完美匹配,则该匹配就是二分图的最大权完美匹配

步骤:

1.初始化,左部顶标=max(出边边权最大值),右部顶标=0
2.枚举左顶点,沿交替路走,寻找增广路
若能找到未匹配的点,记录匹配对点
若不能走到右部一个未匹配点,则记录delta= l a [ x ] + l b [ y ] − w ( x , y ) la[x]+lb[y]-w(x,y) la[x]+lb[y]w(x,y)
根据最小的delta更新交替路上的顶标值(左减右加),总可以找到新的增广路

代码

#include
#include
#include
#include
using namespace std;
#define LL long long
#define N 510
#define INF 1e12
int n,m;
int match[N];//右点匹配了哪个左点
int va[N],vb[N];//标记是否在交替路中
LL la[N],lb[N];//左顶标,右顶标
LL w[N][N],d[N];//维护更新的delta值

bool dfs(int x){
    va[x]=1; //x在交替路中
    for(int y=1;y<=n;y++){
      if(!vb[y]){
          if(la[x]+lb[y]-w[x][y]==0){//相等子图
              vb[y]=1; //y在交替路中
              if(!match[y]||dfs(match[y])){
                match[y]=x; //配对
                return 1;
              }
          }
          else //不是相等子图则记录最小的d[y]
            d[y]=min(d[y],la[x]+lb[y]-w[x][y]);
      }
    }
    return 0;
}
LL KM(){
  //左顶标取i的出边的最大边权
    for(int i=1;i<=n;i++) la[i]=-INF;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++) 
          la[i]=max(la[i],w[i][j]);
    for(int i=1;i<=n;i++) lb[i]=0;      
    for(int i=1;i<=n;i++){
        while(true){//直到左点i找到匹配
            fill(va+1,va+n+1,0);
            fill(vb+1,vb+n+1,0);
            fill(d+1,d+n+1,INF);
            if(dfs(i))break;
          LL delta=INF;
            for(int j=1;j<=n;j++)
              if(!vb[j])delta=min(delta,d[j]);
            for(int j=1;j<=n;j++){//修改顶标
              if(va[j])la[j]-=delta;
              if(vb[j])lb[j]+=delta;
            }
        }
    }
    LL res=0;
    for(int i=1;i<=n;i++)res+=w[match[i]][i];    
    return res;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++) 
          w[i][j]=-INF; 
    for(int i=1;i<=m;i++){
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        w[x][y]=z;
    }
    printf("%lld\n",KM());
    for(int i=1;i<=n;i++) 
      printf("%d ",match[i]);
    return 0;
}

你可能感兴趣的:(算法模板,#,网络流,算法)