今天集训,想起来之前看的一脸懵逼的二分图,打算再捡起来看一次,然后翻到了这个用海贼王解释匈牙利算法的二分图算法,瞬间开朗,但貌似里面有点地方的代码还是不清楚的,下面代码会写得相对详细一点
顺带去做了几道二分图的基础题HDU–2063(由于今天好像HDU挂了,只过了一题)POJ–1274,POJ–2239,POJ–2536
这些题都很水,开头数据处理下直接套匈牙利的版就行了
二分图就不讲了,以下是百度百科的解释
二分图又称作二部图,是图论中的一种特殊模型。 设G=(V,E)是一个无向图,如果顶点V可分割为两个互不相交的子集(A,B),并且图中的每条边(i,j)所关联的两个顶点i和j分别属于这两个不同的顶点集(i in A,j in B),则称图G为一个二分图。
总而言之,就是只能分为两个帮派的图,出现第三个帮派的都不叫二分图
然后二分图有几个基本的概念:
最大匹配数:最大匹配的匹配边的数目
最小点覆盖数:选取最少的点,使任意一条边至少有一个端点被选择
最大独立数:选取最多的点,使任意所选两点均不相连
最小路径覆盖数:对于一个 DAG(有向无环图),选取最少条路径,使得每个顶点属于且仅属于一条路径。路径长可以为 0(即单个点)。
定理1:最大匹配数 = 最小点覆盖数(这是 Konig 定理)
定理2: 最大独立数 = 顶点数 - 最大匹配数
定理3:最小路径覆盖数 = 顶点数 - 最大匹配数
最后附上匈牙利模板,要理解的话请点击上面那个连接=-=这里就不详解了
int map[maxn][maxn]; //记录两者是否有关系
int used[maxn]; //记录是否有用过
int upon[maxn]; //记录他所匹配的人
//下面定义A集合中的某个值为ai,B集合的某个值为bi
bool dfs(int master,int m){ //传入ai和B集合中单个子集的总数
for(int i = 1;i <= m;i++){ //遍历B集合的所有单个子集
if(map[master][i] && !used[i]){ //如果ai和bi有关且bi没有被访问过
used[i] = 1; //标记成访问的
if(upon[i] == -1 || dfs(upon[i],m)){ //如果bi没有被标记成某个ai用过的,或者找找bi相应的ai一直递归下去看看有没有某个bi能换一个ai
upon[i] = master; //记录bi相对应的ai
return true; //找到对象,返回成功
}
}
}
return false; //找不到对象,返回失败
}
int huangary(int n,int m){
int res = 0;
memset(upon,-1,sizeof(upon));
for(int i = 1;i <= n;i++){ //匹配所有解
memset(used,0,sizeof(used));
if(dfs(i,m))
res++;
}
return res; //返回最终解
}
今天集训最后一天,又撸懂了一个算法=-=