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;
}
理解比套多少次模板都强
写一次优化不少的附上详细的解释
匈牙利算法已经指出了从一端(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;
}
相关文章推荐
- hdu 1019
- java list转JSONObject报错
- 前端开发环境配置
- thinkphp 整合phpqrcode 生成二维码
- C#实现汉字转换为拼音缩写的代码
- GaN(第三代器件)特性的总结
- hdu 2473 帮派
- android camera HAL v3.0详细介绍(二)
- (4.2.32.6)android热修复之Andfix方式:Andfix的Hook方式打补丁原理
- iOS开发工程师与UI视觉设计师不得不说的故事
- phpstudy虚拟主机配置
- angularjs相关知识细化
- Valid Parentheses & Longest Valid Parentheses
- Myeclipse2014找不到Help-->install new software
- 记一次基于Unity的Profiler性能分析
- Java的泛型
- Hadoop学习笔记:MapReduce框架详解
- 微信扫码登陆过程。
- SSD:Single Shot MultiBox Detector 心得 (持续更新中。。。)
- 用html、jquery、php编写新闻后台管理页面,动态添加新闻数据并实现数据的增删改查功能