题解:给出一个二分图,问你取点哪个点会使得二分图匹配数减少。

解法:其实就是问二分图匹配的必须点。先对初始二分图做一次最大匹配。

现在考虑左边点,看残余网络上的边重新构图:如果是匹配边那么就从右往左连边,如果是非匹配边就从左往右连边。然后从每个非匹配点出发dfs标记每一个访问过的点。最后是匹配点且未被访问过的就是必须点。为什么呢?仔细考虑新构的图,从未匹配点出发的其实也是一条增广路,如果这条增广路能访问到 已匹配点 。那么我们就可以把这条增广路取反得到另一种匹配方案,也就是说这个被访问到的 已匹配点是非必须的。

右边点原理相似。

细节详见代码以及注释:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int N=+;
const int M=+;
const int INF=0x3f3f3f3f;
int n1,n2,m,s,t,id[M<<];
struct edge{
int nxt,to,cap;
}edges[M<<];
int cnt=,x[M],y[M],head[N],cur[N]; int add_edge(int x,int y,int z) {
edges[++cnt].nxt=head[x]; edges[cnt].to=y; edges[cnt].cap=z; head[x]=cnt;
return cnt;
} int dep[N];
queue<int> q;
bool bfs() {
while (!q.empty()) q.pop();
memset(dep,,sizeof(dep));
dep[s]=; q.push(s);
while (!q.empty()) {
int x=q.front(); q.pop();
for (int i=head[x];i;i=edges[i].nxt) {
edge e=edges[i];
if (!dep[e.to] && e.cap) {
dep[e.to]=dep[x]+;
q.push(e.to);
}
}
}
return dep[t];
} int dfs(int x,int lim) {
if (x==t) return lim;
for (int& i=cur[x];i;i=edges[i].nxt) {
edge e=edges[i];
if (dep[x]+==dep[e.to] && e.cap) {
int flow=dfs(e.to,min(lim,e.cap));
if (flow>) {
edges[i].cap-=flow;
edges[i^].cap=+flow;
return flow; //找到一条增广路就要return
}
}
}
return ;
} int Dinic() {
int maxflow=;
while (bfs()) {
for (int i=s;i<=t;i++) cur[i]=head[i]; //当前弧优化
while (int flow=dfs(s,INF)) maxflow+=flow;
}
return maxflow;
} bool vis[N],match[N]; vector<int> G[N];
bool dfs2(int x) {
vis[x]=;
for (int i=;i<G[x].size();i++) {
int y=G[x][i];
if (!vis[y]) dfs2(y);
}
} int main()
{
scanf("%d%d%d",&n1,&n2,&m);
s=; t=n1+n2+;
for (int i=;i<=m;i++) {
scanf("%d%d",&x[i],&y[i]);
id[i]=add_edge(x[i],y[i]+n1,);
add_edge(y[i]+n1,x[i],);
}
for (int i=;i<=n1;i++) add_edge(s,i,),add_edge(i,s,);
for (int i=;i<=n2;i++) add_edge(n1+i,t,),add_edge(t,n1+i,); Dinic(); //重新构图
for (int i=;i<=n1+n2;i++) vis[i]=,match[i]=,G[i].clear();
for (int i=;i<=m;i++)
if (edges[id[i]].cap) G[x[i]].push_back(n1+y[i]); //不匹配边左到右连边
else G[n1+y[i]].push_back(x[i]),match[x[i]]=; //匹配边右到左连边
for (int i=;i<=n1;i++) if (!match[i]) dfs2(i); //从每个未匹配点出发dfs
for (int i=;i<=n1;i++)
if (match[i] && !vis[i]) printf("%d\n",i); //是匹配点且未被 未匹配点 访问的 for (int i=;i<=n1+n2;i++) vis[i]=,match[i]=,G[i].clear();
for (int i=;i<=m;i++)
if (edges[id[i]].cap) G[n1+y[i]].push_back(x[i]); //不匹配边右到左连边
else G[x[i]].push_back(n1+y[i]),match[n1+y[i]]=; //匹配边左到右连边
for (int i=;i<=n2;i++) if (!match[n1+i]) dfs2(n1+i); //从每个未匹配点出发dfs
for (int i=;i<=n2;i++)
if (match[n1+i] && !vis[n1+i]) printf("%d\n",i); //是匹配点且未被 未匹配点 访问的
return ;
}

BZOJ 3546 [ONTAK2010]Life of the Party (二分图最大匹配必须点)的更多相关文章

  1. BZOJ.3546.[ONTAK2010]Life of the Party(二分图匹配 ISAP)

    题目链接 题意:求哪些点一定在最大匹配中. 这儿写过,再写一遍吧. 求哪些点不一定在最大匹配中.首先求一遍最大匹配,未匹配点当然不一定在最大匹配中. 设一个未匹配点为A,如果存在边A-B,且存在匹配边 ...

  2. bzoj 1854: [Scoi2010]游戏 (并查集||二分图最大匹配)

    链接: https://www.lydsy.com/JudgeOnline/problem.php?id=1854 写法1: 二分图最大匹配 思路:  将武器的属性对武器编号建边,因为只有10000种 ...

  3. BZOJ 3546 Life of the Party (二分图匹配-最大流)

    题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=3546 题意:给定一个二分图.(AB两个集合的点为n,m),边有K个.问去掉哪些点后 ...

  4. 【刷题】BZOJ 3546 [ONTAK2010]Life of the Party

    Description 一个舞会有N个男孩(编号为1..N)和M个女孩(编号为1..M),一对男女能够组成一对跳舞当且仅当他们两个人互相认识. 我们把一种人定义成这个舞会的life:当且仅当如果他(她 ...

  5. [bzoj 1059][ZJOI 2007]矩阵游戏(二分图最大匹配)

    题目:http://www.lydsy.com/JudgeOnline/problem.php?id=1059 分析:不论如何交换,同一行或同一列的点还是同一行或同一列,如果我们称最后可以排成题目要求 ...

  6. BZOJ 4443: [Scoi2015]小凸玩矩阵 二分图最大匹配+二分

    题目链接: http://www.lydsy.com/JudgeOnline/problem.php?id=4443 题解: 二分答案,判断最大匹配是否>=n-k+1: #include< ...

  7. BZOJ 3993: [SDOI2015]星际战争 [二分答案 二分图]

    3993: [SDOI2015]星际战争 题意:略 R1D2T1考了裸二分答案+二分图最大匹配... #include <iostream> #include <cstdio> ...

  8. [bzoj 1143]最长反链二分图最大匹配

    Dilworth定理:偏序集能划分成的最少的全序集的个数与最大反链的元素个数相等. 证明:http://www.cnblogs.com/itlqs/p/6636222.html 题目让求的是最大反链的 ...

  9. POJ 2226二分图最大匹配

    匈牙利算法是由匈牙利数学家Edmonds于1965年提出,因而得名.匈牙利算法是基于Hall定理中充分性证明的思想,它是二部图匹配最常见的算法,该算法的核心就是寻找增广路径,它是一种用增广路径求二分图 ...

随机推荐

  1. webpack插件之html-webpack-plugin

    官方文档:https://www.npmjs.com/package/html-webpack-plugin html-webpack-plugin 插件专门为由webpack打包后的js提供一个载体 ...

  2. 箭头函数与普通function的区别

    1. 箭头函数没有自己的this,它里面的this是继承所属上下文中的this,而且使用call与apply都无法改变 let obj = { name: 'obj' } function fn1() ...

  3. 用私有构造器或者枚举类型强化SingleTon(单例)属性

    单例(singleton)就是一个只实例化一次的类.使类成为单例可能会使它的测试变得困难,因为除非它实现了作为其类型的接口,否则不可能用模拟实现来代替这个单例.下面是几种实现单例的方法: 1.共有静态 ...

  4. WaitForSingleObject的作用[转]

    在多线程的情况下,有时候我们会希望等待某一线程完成了再继续做其他事情(比如主线程等待子线程结束完之后,自己再结束),要实现这个目的,可以使用Windows API函数WaitForSingleObje ...

  5. 【串线篇】SpringMvc数据传出

    /** * SpringMVC除过在方法上传入原生的request和session外还能怎么样把数据带给页面 *  * 四大域: *  pageContext:${pageScope.msg }< ...

  6. shell 判断字符串是否包含另一个字符串

    1.使用grep s1="abcdefg" s2="bcd" result=$(echo $s1 | grep "${s2}") if [[ ...

  7. 贾扬清谈大数据&AI发展的新挑战和新机遇

    摘要:2019云栖大会大数据&AI专场,阿里巴巴高级研究员贾扬清为我们带来<大数据AI发展的新机遇和新挑战>的分享.本文主要从人工智能的概念开始讲起,谈及了深度学习的发展和模型训练 ...

  8. Druid数据源配置入门

    Druid是什么 Druid首先是一个数据库连接池.Druid是目前最好的数据库连接池,在功能.性能.扩展性方面,都超过其他数据库连接池,包括DBCP.C3P0.BoneCP.Proxool.JBos ...

  9. px4的CMakelists.txt阅读

    ############################################################################ # # Copyright (c) PX4 D ...

  10. 切换路由时取消全部或者部分axios请求,并将一些从不需要取消的加入白名单

    1. axios拦截器进行配置,除了白名单中的接口,统统保存到全局变量canCancelAxios中 window.canCancelAxios = []; // http request 拦截器 a ...