题目大意:
有一个n*n的棋盘,给出一些点的位置,询问最少去掉多少行和列,可以把所有点去掉,输出行列总数
分析:
这道题,棋盘,点,行,列,有没有想到什么??
没错
二分图………….
最大匹配………..
很惭愧(⊙﹏⊙)b,自己太弱了,对二分图理解不够深刻,没看出来这是个二分图>_<,在YOUSIKI童鞋的友情提示下才知道此题要用二分图解(ˇˍˇ) …………….
所以接下来就要说一说怎么求二分图最大匹配
锵锵锵锵!!!(^o^)/~匈牙利算法闪亮登场
先来说一说匈牙利算法的最核心思想吧—–找增广路
(⊙o⊙)…额,什么是增广路?_?
先要说一下什么叫做交替路:
在已有匹配P的二分图中,存在路径S,v1、v2、v3、v4……∈S,v1∈P,v2∉P,v3∈P,v4∉P……,那么S就是一条交替路
所谓增广路,就是起点和终点都∉P的一条交替路
下图中,9->4->8->1->6->2就是一条增广路
增广路一定是奇数条边,因为除了起点和终点其他点都是匹配点,一定存在奇数条边(偶数条匹配边,这是显然的,奇数条未匹配边),再加上由起点和终点衍生出来的两条未匹配边,一定是奇数条边……..
仔细观察,我们会发现,增广路中未匹配路径比匹配路径要多一条,这是很显然的……………因为刚刚我们说明了,除了起点和终点其他点都是匹配点,一定存在奇数条边(偶数条匹配边,这是显然的,奇数条未匹配边),并且未匹配边的个数比匹配边的个数少一,再加上由起点和终点衍生出来的两条未匹配边,所以未匹配边的个数比匹配边的个数多一…………..
有了以上性质,我们就可以计算最大匹配了
首先说有向边:
每一次从一个点出发,寻找增广路,如果找到了增广路,证明匹配数需要+1………………然后就没有然后了
等等!!有个重要问题!!怎么找增广路呢??
依次考虑每个左部节点,为其找到一个右部节点与之匹配。
一个右部节点能与之匹配,必满足一下两个条件之一:
1、这个节点尚未与任何左部节点匹配,此时直接把两个节点进行匹配
2、从该节点匹配的左部节点出发,可以找到一个未标记的右部节点与之匹配,此时给该节点打上标记,递归进入那个左部节点,为它寻找匹配节点
代码如下:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxn=500+5,maxm=10000+5;
int n,k,hd[maxn*2],to[maxm],nxt[maxm],ans,vis[maxn*2],cnt,pre[maxn*2];
inline int read(void){
char ch=getchar();
int f=1,x=0;
while(!(ch>='0'&&ch<='9')){
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
x=x*10+ch-'0',ch=getchar();
return f*x;
}
inline void add(int x,int y){
to[cnt]=y;
nxt[cnt]=hd[x];
hd[x]=cnt++;
}
bool dfs(int u){
for(int i=hd[u];i!=-1;i=nxt[i]){
if(!vis[to[i]]){
vis[to[i]]=true;
if(pre[to[i]]==-1||dfs(pre[to[i]])){
pre[to[i]]=u;
return true;
}
}
}
return false;
}
signed main(void){
memset(hd,-1,sizeof(hd)),cnt=0;
memset(pre,-1,sizeof(pre));
n=read(),k=read();
while(k--)
add(read(),read()+n);
for(int i=1;i<=n;i++){
memset(vis,0,sizeof(vis));
if(dfs(i))
ans++;
}
cout<<ans<<endl;
return 0;
}
接下来说无向边:
考虑每个未匹配的左部节点,为其寻找一个可以匹配的右部节点,右部节点的匹配条件相同
这里借用一下YOUSIKI童鞋的代码(自己懒得再写一遍了(⊙o⊙)…懒癌晚期……….),希望YOUSIKI童鞋不要收版权费>_<
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1005,M=20005;
int n,m,hd[N],to[M],nt[M],tot,mch[N],chk[N];
inline void addEdge(int x,int y){nt[tot]=hd[x],to[tot]=y,hd[x]=tot++;}
inline bool dfs(int u){
for(int i=hd[u];~i;i=nt[i])if(!chk[to[i]]){
chk[to[i]]=1;if(mch[to[i]]==-1||dfs(mch[to[i]])){
mch[u]=to[i],mch[to[i]]=u;return true;}
}return false;
}
signed main(void){
scanf("%d%d",&n,&m);
memset(hd,-1,sizeof(hd)),tot=0;
for(int i=1,x,y;i<=m;i++)
scanf("%d%d",&x,&y),y+=n,
addEdge(x,y),addEdge(y,x);
int ans=0;memset(mch,-1,sizeof(mch));
for(int i=1;i<=n;i++)if(mch[i]==-1){
memset(chk,0,sizeof(chk));
if(dfs(i))ans++;
}printf("%d\n",ans);
}//代码好短>_<.............
by >o< neighthorn

308

被折叠的 条评论
为什么被折叠?



