#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;
}
推荐视频
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;
}