文章目录
- 题目描述
- 解析
- 代码
题目描述
在一个n*n的矩阵中,有 k个格子中有杂物,现在你有一种能力,一次可以消除一行或一列格子中的杂物,问你至少需要几次可以将这些杂物全部消完。
解析
看起来像是网络流,结果竟然是二分图。。。
本题关键是模型的转化,思路有了后就变成板子了
考虑做法:
对于每一个点(x,y)(x,y)(x,y),都连一条从x到y的边,最后求最大匹配就是答案
为什么是这样?
首先,这样连边之后,每个点对应一条边,因此每条边至少要有一个点被选到,因此相当与求这个图的最小点覆盖
然后就是两个很妙的结论:
1.最小点覆盖=n-最大独立集
2.最大独立集=n-最大匹配
为什么?
(网上没有找到证明,只好自力更生了qwq)
首先,关于性质1,比较简单:对于任何一个点独立集SSS,它的补集S′S'S′都是一个合法的点覆盖(因为根据独立集的定义,不存在一条边的两个端点都在SSS中),那么要使点覆盖最小,就要使独立集最大
对于性质2,设全集为M,最大匹配的点集为P,最大独立集的点集为S,规定一个点集X的大小用|X|表示
那么最大匹配就是∣P∣/2|P|/2∣P∣/2
我们考虑分为两步证明:∣S∣<=∣M∣−∣P∣/2|S|<=|M|-|P|/2∣S∣<=∣M∣−∣P∣/2 和 ∣S∣>=∣M∣−∣P∣/2|S|>=|M|-|P|/2∣S∣>=∣M∣−∣P∣/2
- ∣S∣<=∣M∣−∣P∣/2|S|<=|M|-|P|/2∣S∣<=∣M∣−∣P∣/2 :比较显然,考虑在匹配中的每一对点,至少有一个点不在独立集中(因为它们互相连接),所以独立集的大小一定不会超过∣M∣−∣P∣/2|M|-|P|/2∣M∣−∣P∣/2
- ∣S∣>=∣M∣−∣P∣/2|S|>=|M|-|P|/2∣S∣>=∣M∣−∣P∣/2 :设最大匹配的补集为P′P'P′,那么首先P‘的所有点可以加入S(否则它们就可以匹配了),然后对于P中的每一对匹配点A、B,考虑它们一定不能同时与P′P'P′中的点有连边(否则就可以增广了,不是最大匹配),所以它们中至少一个可以加入SSS中。
证毕
有了上面的分析后,本题就变成了一个求最大匹配的板子了
代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e6+100;
#define ll long long
ll read(){ll x=0,f=1;char c=getchar();while(!isdigit(c)){if(c=='-')f=-1;c=getchar();};while(isdigit(c)){x=x*10+c-'0';c=getchar();};return x*f;
}
int n,m;
struct node{int to,nxt;
}p[N<<1];
int fi[N],cnt=-1;
void addline(int x,int y){p[++cnt]=(node){y,fi[x]};fi[x]=cnt;
}
int mat[N],vis[N];
bool hungry(int x,int tim){if(vis[x]==tim) return false;vis[x]=tim;for(int i=fi[x];~i;i=p[i].nxt){int to=p[i].to;if(!mat[to]||dfs(mat[to],tim)){mat[to]=x;return true;}}return false;
}
int main(){memset(fi,-1,sizeof(fi));n=read();m=read();for(int i=1;i<=m;i++){int x=read(),y=read();addline(x,y);}int res=0;for(int i=1;i<=n;i++){if(dfs(i,i)) res++;}printf("%d\n",res);return 0;
}
/*
3 4
1 1
1 3
2 2
3 2
*/