您的位置:首页 > 其它

UVa 11419 二分图最小点覆盖 续写 好题

2016-07-13 11:03 141 查看
第二次写这个还是一塌糊涂,说实话算法学了只会一个模板真的一点用也没有,需要不断的去再写再学再写才能巩固。

理解比套多少次模板都强

写一次优化不少的附上详细的解释

匈牙利算法已经指出了从一端(A)的点出发寻找增广路找到最大的匹配数

Konig定理指出最大匹配数等于最小点覆盖

问题在于怎么输出这些点

我们用r数组记行c数组记列

当从已经匹配的图中A端未匹配的点出发寻找增广路标记沿途上的点,则A端未被标记和另一端(B)已经标记的点为最小覆盖点也就是核心所在

#include<cstdio>
#include<iostream>
#include<cstring>
#include<queue>
#include<string>
#define min(a,b) a>b?b:a
#define text cout<<"text"<<endl;
using namespace std;
typedef long long LL;
int mm[1100][1100];//邻接矩阵
int dis[1100],vis[1100];//dis表示的是B端连接A端中的某点为已经匹配的点
int n,m,p;
int have[1010];//表示的是A端已经被最大匹配中的点所标记的点
int r[1010],c[1010];
int dfs(int v){
r[v]=1;//寻找最少的点时标记A端覆盖的点
for(int u=1;u<=m;u++){
if(mm[v][u]&&!vis[u]){//
c[u]=1;//寻找最少的点时标记B端覆盖的点
vis[u]=1;//每次寻找增广路的时候都要标记已经走过的点
if(dis[u]==-1||dfs(dis[u])){//如果B端的点为已经匹配得点,则寻找B端已经匹配在A端的点继续寻找增广路,若B端的点未匹配直接标记B短的点所匹配的点为该店
dis[u]=v;//将B端的点对应为A端的点匹配
have[v]=1;//*****核心:若A端已经被匹配进去则标记该*****
return 1;
}
}
}
return 0;
}
int main(){
while(cin>>n>>m>>p&&(n+m+p)){
memset(mm,0,sizeof(mm));
memset(have,-1,sizeof(have));
memset(dis,-1,sizeof(dis));
for(int i=0;i<p;i++){
int x,y;
scanf("%d%d",&x,&y);
mm[x][y]=1;
}
int sum=0;
for(int i=1;i<=n;i++){//从A端出发寻找增广路
memset(vis,0,sizeof(vis));
if(dfs(i)) sum++;//每找到一条增广路则记下
}
cout<<sum;
memset(r,0,sizeof(r));
memset(c,0,sizeof(c));//这里很重要,在寻找增广路时已经标记,所以寻找点时要重新标记
for(int i=1;i<=n;i++){
memset(vis,0,sizeof(vis));
if(have[i]==-1)dfs(i);//A端未匹配的点进行增广路来标记
}
for(int i=1;i<=m;i++)
if(c[i])cout<<" c"<<i;//输出B端已经标记的点
for(int i=1;i<=n;i++)
if(!r[i])cout<<" r"<<i;//输出A端未被标记的点
cout<<endl;
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: