D176 二分图最大匹配 匈牙利算法 CF1139E Maximize Mex

D176 二分图最大匹配 匈牙利算法 CF1139E Maximize Mex_哔哩哔哩_bilibili

 

CF1139E Maximize Mex - 洛谷         Problem - 1139E - Codeforces

n 个学生,m 个社团。每个学生属于一个社团。在接下来的 d 天里,每天会有一个人退团。

每天从每个社团中最多选出一个人,使得选出的人的潜力值集合 {pi} 的 mex 值最大。求出每天的最大 mex 值。

集合 S 的 mex 定义为不在 S 中的最小非负整数。例如,{0,1,1,2,4,5,9} 的 mex 是 3,{1,2,3} 的 mex 是 0。

思路

先不考虑删除人,怎么做?

每个人的属性是潜力值,每天每个社团中最多选出一个人,答案是选出的潜力值中的最小非负整数

把潜力值作为左部点,把社团作为右部点,连有向边,这样,一个社团最多只能选一条边

从 0 开始枚举潜力值,跑二分图最大匹配,遇到配对失败就终止,此数即答案

如果考虑删除人,相当于删除边,会破坏已有的匹配,不好进行

可以逆序思考,改为加边,每次加一条边,就找一次增广路,那么答案会单调不降

参考:D25【模板】二分图最大匹配 匈牙利算法 - 董晓 - 博客园

   D175 二分图最大匹配 匈牙利算法 P1640 [SCOI2010] 连续攻击游戏 - 董晓 - 博客园

 

// 二分图 匈牙利算法 O(V*E)
#include<bits/stdc++.h>
using namespace std;

const int N=5005;
vector<int> e[N];
int n,m,d,a[N],b[N],c[N],f[N],ans[N];
int vis[N],match[N];

bool dfs(int u){
  for(int v:e[u]){
    if(vis[v]) continue;
    vis[v]=1;
    if(match[v]==-1 || dfs(match[v])){
      match[v]=u;
      return 1; //配对成功
    }
  }
  return 0; //配对失败
}
int main(){
  cin>>n>>m; //学生数 社团数
  for(int i=1; i<=n; i++) scanf("%d",&a[i]); //潜力值
  for(int i=1; i<=n; i++) scanf("%d",&b[i]); //社团编号
  cin>>d;
  for(int i=1; i<=d; i++) scanf("%d",&c[i]); //离团学生
  for(int i=1; i<=d; i++) f[c[i]]=1; //离团学生打标记
  for(int i=1; i<=n; i++)if(!f[i]){  //枚举没有离团的学生
    if(a[i]>m) continue; //潜力值>社团数,不会成为答案
    e[a[i]].push_back(b[i]); //潜力值→社团编号
  }
  
  memset(match,-1,sizeof(match)); //因为有0点,初始化为-1
  for(int i=d; i; i--){ //倒序加边
    for(int j=ans[i+1]; j<=m; j++){ //枚举潜力值
      memset(vis,0,sizeof(vis));
      if(!dfs(j)){ans[i]=j; break;} //潜力值中断,记录答案
    }
    
    if(a[c[i]]>m) continue; //潜力值>社团数,不会成为答案
    e[a[c[i]]].push_back(b[c[i]]); //潜力值→社团编号
  }
  
  for(int i=1; i<=d; i++) printf("%d\n",ans[i]);
  return 0;
}

 

posted @ 2026-05-31 18:38  董晓  阅读(43)  评论(0)    收藏  举报