一、匈牙利算法
解决二分图中的最大匹配问题(很多时候都可以把题目转变成二分图,刚学网络流有点头大)。两个互不相交的子集V1 ,V2 寻找他们能匹配到的最大数量。(由于我菜鸡,仅仅给出几种模板,网上有很多博客讲的很好了)
1、邻接表实现(O( n^3)
模板秒切
#include <iostream> #include <cstdio> #include <cstring> using namespace std; const int N=1001; int n1,n2,k; //n1,n2为二分图的顶点集,其中x∈n1,y∈n2 int map[N][N],vis[N],link[N]; //link记录n2中的点y在n1中所匹配的x点的编号 int find(int x) { int i; for(i=1;i<=n2;i++) { if(map[x][i]&&!vis[i])//x->i有边,且节点i未被搜索 { vis[i]=1;//标记节点已被搜索 //如果i不属于前一个匹配M或被i匹配到的节点可以寻找到增广路 if(link[i]==0||find(link[i])) { link[i]=x;//更新 return 1;//匹配成功 } } } return 0; } int main() { int i,x,y,s=0; while(~scanf("%d",&k)&&k) { s=0; scanf("%d%d",&n1,&n2); memset(map,0,sizeof(map)); memset(link,0,sizeof(link)); for(i=0;i<k;i++) { scanf("%d%d",&x,&y); map[x][y]=1; } for(i=1;i<=n1;i++) { memset(vis,0,sizeof(vis)); //每次记得初始化 if(find(i)) s++; } printf("%d\n",s); } return 0; }
![点击并拖拽以移动](https://img-blog.csdnimg.cn/2022010614262731191.gif)
2、邻接表实现(O(n*m))
一样的思路,不过是改成了邻接表,数据较大(直接记这个就好了)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
#include<stdio.h> #include<string.h> #include<string> #include<algorithm> #include<queue> #define Max 500 #define inf 0x3f3f3f3f using namespace std; void show(int *a,int n); struct Node{ int next; int to; }edge[Max*Max]; int num_edge; int head[Max]; void add_edge(int x,int y) //邻接表 { edge[++num_edge].next=head[x]; edge[num_edge].to=y; head[x]=num_edge; } bool vis[Max];//是否匹配过了 int link[Max]; bool find(int num) { int i,u; for(i=head[num];i;i=edge[i].next) //邻接表访问 { u=edge[i].to; if(!vis[u]) { vis[u]=1; if(!link[u]||find(link[u])) { link[u]=num; return true; } } } return false; } int main() { int t,n,cnt1,cnt2,x; scanf("%d",&t); while(t--) { memset(head,0,sizeof(head)); memset(edge,0,sizeof(edge)); memset(link,0,sizeof(link)); num_edge=0; cnt1=0; cnt2=0; scanf("%d",&n); for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { scanf("%d",&x); if(x) { cnt1++; add_edge(i,j+n); //行 } } } if(cnt1<n) { printf("No\n"); continue; } for(int i=1;i<=n;i++) { memset(vis,0,sizeof(vis)); if(find(i)) cnt2++; } if(cnt2>=n) printf("Yes\n"); else printf("No\n"); } return 0; }
![点击并拖拽以移动](https://img-blog.csdnimg.cn/2022010614262731191.gif)
3、需要自己根据题目构建一个二分图
简单练习:
AC代码:
#include<stdio.h> #include<string.h> #include<string> #include<algorithm> #include<queue> #include<math.h> #define Max 450 #define inf 0x3f3f3f3f using namespace std; struct Node{ int x; int y; int id; double dis; }p[400],b[Max]; struct Edge{ int next; int to; }edge[Max*Max]; bool vis[Max]; int link[Max]; int head[Max]; int ans[Max]; int num_edge; double get_dis(int x1,int y1,int x2,int y2) //计算两点的距离 { return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)); } void add_edge(int x,int y) { edge[++num_edge].next=head[x]; edge[num_edge].to=y; head[x]=num_edge; } bool find(int num) //模板 { int i,u; for(i=head[num];i;i=edge[i].next) { u=edge[i].to; if(!vis[u]) { vis[u]=1; if(!link[u]||find(link[u])) { link[u]=num; ans[num]=u; return true; } } } return false; } int main() { int n,m,x,y,cnt; double d1,d2; struct Node temp; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%d %d",&p[i].x,&p[i].y); p[i].id=i; if(i>1) //获得距离 { p[i-1].dis=get_dis(p[i-1].x,p[i-1].y,p[i].x,p[i].y); //计算前后两个目标点的距离 } } for(int i=1;i<=m;i++) { scanf("%d%d",&x,&y); b[i].id=i; b[i].x=x; b[i].y=y; for(int j=1;j<n;j++) // { d1=get_dis(x,y,p[j].x,p[j].y); d2=get_dis(x,y,p[j+1].x,p[j+1].y); if(d1+d2<=2*p[j].dis) //可以赶回来 就把他们连起来 { // printf("%d %d %d\n",j,j+1,i); add_edge(j,n+i); } } } cnt=0; for(int i=1;i<=n;i++) { memset(vis,0,sizeof(vis)); if(find(i)) cnt++; } printf("%d\n",cnt+n); for(int i=1;i<=n;i++) { printf("%d %d",p[i].x,p[i].y); if(i!=n) printf(" "); if(i!=n&&ans[i]) { printf("%d %d ",b[ans[i]-n].x,b[ans[i]-n].y); } } printf("\n"); return 0; }
![点击并拖拽以移动](https://img-blog.csdnimg.cn/2022010614262731191.gif)
二、KM算法
求带权值的二分图的最优匹配(还是直上模板和题目,还是给个链接吧,网上貌似很多,图都一样。。。。)
练习题:
奔小康赚大钱
模板:
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 #include<string> 5 #include<queue> 6 #include<stack> 7 #include<map> 8 #include<algorithm> 9 #define Max 305 10 #define inf 0x3f3f3f3f 11 #define max(a,b) a>b?a:b; 12 13 using namespace std; 14 int min(int x,int y) 15 { 16 if(x>y) return y; 17 else return x; 18 } 19 int love[Max][Max];//记录好感度 20 int e_girl[Max];//记录期望值 21 int e_boy[Max]; 22 bool vis_girl[Max]; //女孩是否匹配过 23 bool vis_boy[Max]; //男孩是否匹配过 24 int match[Max]; //记录匹配到的对象,boy匹配到的girl 25 int slack[Max]; //记录需要增加或者减少的标记 26 int N; //最大的人数 27 bool dfs(int girl) //匈牙利求匹配 28 { 29 vis_girl[girl]=true; 30 for(int i=0;i<N;i++) 31 { 32 if(!vis_boy[i]) 33 { 34 int gap=e_girl[girl]+e_boy[i]; 35 if(gap==love[girl][i]) //如果标签和边的值相等,尝试匹配 36 { 37 vis_boy[i]=true; 38 if(match[i]==-1||dfs(match[i])) //如果没有匹配或者可以找到其他人 39 { 40 match[i]=girl; 41 return true; 42 } 43 } 44 else 45 { 46 //还差多少能获得该对象,用于更新标签,也就是期望 47 slack[i]=min(slack[i],gap-love[girl][i]); 48 } 49 } 50 } 51 return false; 52 } 53 int KM() 54 { 55 memset(match,-1,sizeof(match)); 56 memset(e_boy,0,sizeof(e_boy)); 57 for(int i=0;i<N;i++) 58 { 59 e_girl[i]=love[i][0]; //找到初始期望,所有边值最大的那个 60 for(int j=1;j<N;j++) 61 { 62 e_girl[i]=max(e_girl[i],love[i][j]); 63 } 64 } 65 for(int i=0;i<N;i++) 66 { 67 memset(slack,inf,sizeof(slack)); //每次刷新期望 68 while(1) 69 { 70 memset(vis_girl,false,sizeof(vis_girl)); 71 memset(vis_boy,false,sizeof(vis_boy)); 72 if(dfs(i)) break;//找到一个pass 73 int d=inf; 74 for(int j=0;j<N;j++) 75 { 76 //找到没有匹配过最小的期望,更新,保证每次更新后能再次匹配 77 if(!vis_boy[j]) d=min(d,slack[j]); 78 } 79 for(int j=0;j<N;j++) 80 { 81 if(vis_girl[j]) e_girl[j]-=d; //女生降低期望 82 if(vis_boy[j]) e_boy[j]+=d; //男生提高要求 83 else slack[j]-=d; //没有访问过 因为女生期望降低,所以slack减少 84 } 85 86 } 87 } 88 int res=0; 89 for(int i=0;i<N;i++) 90 { 91 res+=love[match[i]][i]; 92 } 93 return res; 94 } 95 int main() 96 { 97 while(scanf("%d",&N)!=EOF) 98 { 99 for(int i=0;i<N;i++) 100 { 101 for(int j=0;j<N;j++) 102 { 103 scanf("%d",&love[i][j]); 104 } 105 } 106 printf("%d\n",KM()); 107 } 108 return 0; 109 }
![点击并拖拽以移动](https://img-blog.csdnimg.cn/2022010614262731191.gif)
三、稳定婚姻匹配(GS算法)
核心就是不断找女朋友 (所以又叫女朋友算法,)
思路很简单,但是需要定义很多变量,反正就是一直找,直到全部遍历完成或者全部配对成功。
模板练习:
[国家集训队]稳定婚姻
模板(使用了map记录名字):
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 #include<string> 5 #include<queue> 6 #include<stack> 7 #include<map> 8 #include<iostream> 9 #include<algorithm> 10 #define Max 510 11 #define max(a,b) a>b?a:b; 12 #define min(a,b) a>b?b:a; 13 14 using namespace std; 15 map<string ,int > boy;//因为名字可能相同,。。。 16 map<string ,int > girl; 17 map<string ,int >::iterator it; 18 string boyName[Max],girlName[Max];//男女的名字 19 int relation[Max][Max]; 20 int manpos[Max]; 21 int boyMatch[Max]; 22 int girlMatch[Max]; 23 24 int boyList[Max][Max];//男孩清单 25 int girlList[Max][Max];//女孩清单 26 int cnt; 27 int a[Max]; 28 queue<int> q;//队列 29 int main() 30 { 31 string temp1,temp2,temp;//转化字符 32 int n; 33 char name[100],name1[100];//名字输入 34 while(scanf("%d",&n)!=EOF) 35 { 36 cnt=0; 37 boy.clear(),girl.clear(); 38 for(int i=1;i<=n;i++) 39 { 40 scanf("%s",name); 41 temp1=name; 42 boyName[i]=temp1;//记录 43 boy[temp1]=i; 44 for(int j=1;j<=n;j++) 45 { 46 scanf("%s",name1); 47 temp2=name1; 48 if(!girl[temp2]) 49 { 50 girl[temp2]=++cnt; 51 girlName[cnt]=temp2; 52 } 53 boyList[i][j]=girl[temp2];//转化为数字 54 relation[i][girl[temp2]]=j;//i 到j 的优先级为 j 55 56 } 57 } 58 for(int i=1;i<=n;++i) 59 { 60 scanf("%s",name); 61 temp=name; 62 int id=girl[temp]; //找到女孩的编号 63 for(int j=1;j<=n;j++) 64 { 65 scanf("%s",name1); 66 int boy_id=boy[name1];// 67 girlList[id][j]=boy_id; 68 relation[id][boy_id]=j; 69 } 70 } 71 while(q.size()) q.pop(); 72 for(int i=1;i<=n;i++) 73 { 74 q.push(i);//把所有的男生全部压人 75 } 76 for(int i=1;i<=n;i++) 77 { 78 boyMatch[i]=girlMatch[i]=-1;//-1表示没有匹配 79 manpos[i]=0; 80 } 81 while(q.size()) 82 { 83 int s=q.size();//目前还有多少人没匹配 84 for(int i=1;i<=s;i++) 85 { 86 int now=q.front();//获得男孩序号 87 ++manpos[now]; //匹配次数增加 88 q.pop(); 89 if(girlMatch[boyList[now][manpos[now]]]==-1) //没男朋友 90 { 91 girlMatch[boyList[now][manpos[now]]]=now;//匹配 92 boyMatch[now]=boyList[now][manpos[now]];// 记录 93 } 94 else //比较优先级 95 { 96 if(relation[boyList[now][manpos[now]]][now]<relation[boyList[now][manpos[now]]][girlMatch[boyList[now][manpos[now]]]]) 97 { 98 boyMatch[girlMatch[boyList[now][manpos[now]]]]=-1;//把前男友鸽了 99 q.push(girlMatch[boyList[now][manpos[now]]]); 100 girlMatch[boyList[now][manpos[now]]]=now;//和最新的在一起 101 boyMatch[now]=boyList[now][manpos[now]]; 102 } 103 else //还是和原来的在一起 104 { 105 q.push(now);//本次匹配失败 106 } 107 } 108 } 109 } 110 for(it=boy.begin();it!=boy.end();++it) 111 { 112 temp=it->first; 113 temp1=girlName[boyMatch[it->second]]; 114 cout<<temp<<" "<<temp1<<endl; 115 } 116 } 117 return 0; 118 }
![点击并拖拽以移动](https://img-blog.csdnimg.cn/2022010614262731191.gif)
三种匹配算法基础模板都在这了,不过还需要更多的题目训练自己。(稳定婚姻算法还获得了2012诺贝尔经济学奖,感觉我也会,狗头~~~)
感觉现在网络流和点分治一起看有点慢啊,在机房学习效率真低,还不如晚上自己一小时。加油吧 还有半个月,省选题目尽量多刷,树和图我都要!