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

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

现在考虑左边点,看残余网络上的边重新构图:如果是匹配边那么就从右往左连边,如果是非匹配边就从左往右连边。然后从每个非匹配点出发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. c# 编程--结构体

    结构体:由多种简单类型,组合成一种复杂的类型.使用这种复杂的类型来解决生活中的实际例子. 一.结构体定义:struct 结构体的名子{    public 类型名 变量名;    .....}    ...

  2. vue项目的脚手架

    > cnpm i @vue/cli@3 -g > vue create myapp * 选择 Manually select features ----- 自选预设文件 * 选择 vue ...

  3. javascript基础7(正则及表单验证)

    1.正则的概念     JS诞生的目的是什么?     就是为了做表单验证.       在JS未出现以前,表单的信息验证需要传输给后台,让后台做数据验证处理之后,再返回给前端页面处理的结果.在带宽有 ...

  4. 灵活轻便的Table控件,适合复杂样式的内容排版

    github仓库地址 https://github.com/gaoyangclub/GYTableViewController 前言 TableView是在项目开发的时候经常用到的组件,几乎百分之八十 ...

  5. JS数字转字符串的方法(number to string)

    一.双点解析 ..toString(); 二.括号先计算再转换 ().toString(); 三.加空串 + ''

  6. Mysql中存储过程和函数的写法

    MySQL中,创建存储过程的基本形式如下: CREATE PROCEDURE sp_name ([proc_parameter[,...]]) [characteristic ...] routine ...

  7. 阿里云Linux服务器购买、配置

    购买.配置阿里云Linux服务器配置ftp发布网站全教程 http://blog.csdn.net/Jolesen/article/details/77505840

  8. js打印窗口内容并当窗口内容较长时自动分页

    项目环境Angular: 方法1.window.print() HTML页面上的代码: <div id="tenementBillTable" class="dia ...

  9. Unicode数据类型的是是非非(转)

    转:http://cio.chinabyte.com/344/9002344.shtml 在SQL Server数据库中,数据类型主要分为两类,分别为Unicode数据类型与非Unicode数据类型. ...

  10. ICO和区块链区别

    区块链项目众筹(ICO)浅析 2017-07-25 原创 Fintech科普大使 ICO是区块链初创公司项目融资的重要方式类似于Kickstarter众筹,但有不同之处(具体在下一节详述),可以避开传 ...