题目大意:
有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