题目大意:
有AB两台机器,A有n个模式0~n-1,B有m个模式0~m-1,现在你需要完成k个任务,每个任务有两个选择,A的i模式或者B的j模式,每台机器可以随意变换模式,但是变换模式需要重启机器,现在希望以最少的重启次数完成任务,请你输出最少输出重启次数
分析:
此题二分图,最小覆盖
这是第三遍写此题了……….
第一遍写的最大匹配……..
第二遍写的最小覆盖……..
第三遍是为了复习最小覆盖……….
复习一下怎么寻找最小覆盖吧!!
满足图中的每一条边都至少有一个点在其中的点集就是图的覆盖,顾名思义,最小覆盖就是点数就最少的覆盖
首先求出图的最大匹配,从每一个左部未匹配节点出发,寻找交错路,标记访问过的节点,取左部未标记节点和右部标记节点,就是最小覆盖
为什么捏??
首先我们可以证明这些点都是匹配点
左部未匹配点一定被标记了(这是显然的),右部未匹配节点一定未被访问(因为最大匹配中不存在增广路,增广路两端节点为未匹配点)……….>_<
如果一条路径属于最大匹配,那么它两端的节点要么都被标记,要么都未被标记,(因为如果i被标记,那么一定会递归进它的匹配点—这是寻找交错路的过程),那么如果一条属于最大匹配的路径,左部节点被标记,右部节点一定也被标记,这时我们选择左部未被标记的和右部被标记的,这样每条路径一定有且只有一个节点属于我们所选取的点集,就是最小覆盖啦~(≧▽≦)/~
代码如下:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
using namespace std;
const int maxn=100+5;
int n,m,k,hd[maxn],to[maxn*maxn],nxt[maxn*maxn],cnt,vis[maxn*2],pre[maxn*2],ans;
int tol[maxn*2],tor[maxn*2],markl[maxn*2],markr[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){
if(x==0||y==n)
return;
to[cnt]=y;
nxt[cnt]=hd[x];
hd[x]=cnt++;
}
inline void find(int u){
markl[u]=1;
for(int i=hd[u];i!=-1;i=nxt[i])
if(tor[to[i]]&&!markr[to[i]]){
markr[to[i]]=1;
find(tor[to[i]]);
break;
}
}
inline bool dfs(int u){
for(int i=hd[u];i!=-1;i=nxt[i])
if(!vis[to[i]]){
vis[to[i]]=1;
if(pre[to[i]]==-1||dfs(pre[to[i]])){
pre[to[i]]=u;
return true;
}
}
return false;
}
signed main(void){
while(n=read()){
cnt=ans=0,memset(hd,-1,sizeof(hd));
memset(pre,-1,sizeof(pre));
m=read(),k=read();
while(k--){
int s=read(),x=read(),y=read();
add(x,y+n);
}
for(int i=1;i<n;i++){
memset(vis,0,sizeof(vis));
dfs(i);
}
memset(tor,0,sizeof(tor)),memset(tol,0,sizeof(tol));
memset(markl,0,sizeof(markl)),memset(markr,0,sizeof(markr));
for(int i=n+1;i<m+n;i++)
if(pre[i]!=-1)
tor[i]=pre[i],tol[pre[i]]=i;
for(int i=1;i<n;i++)
if(!tol[i]&&!markl[i])
find(i);//从每一个未被标记的未匹配的左部节点出发寻找交错路
for(int i=1;i<n;i++)
if(!markl[i])//取左部未标记节点--一定是匹配点
ans++;
for(int i=1+n;i<m+n;i++)
if(markr[i])//取右部标记节点--一定是匹配点
ans++;
printf("%d\n",ans);
}
return 0;
}
by >o< neighthorn

本文深入探讨了二分图中的最小覆盖问题,通过三次实践详细解释了如何从最大匹配出发找到最小覆盖集合的方法,并附带提供了完整的C++实现代码。
1007

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



