一、匈牙利算法
解决二分图中的最大匹配问题(很多时候都可以把题目转变成二分图,刚学网络流有点头大)。两个互不相交的子集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;
}
2、邻接表实现(O(n*m))
一样的思路,不过是改成了邻接表,数据较大(直接记这个就好了)。


#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;
}
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;
}
二、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 }
三、稳定婚姻匹配(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 }
三种匹配算法基础模板都在这了,不过还需要更多的题目训练自己。(稳定婚姻算法还获得了2012诺贝尔经济学奖,感觉我也会,狗头~~~)
感觉现在网络流和点分治一起看有点慢啊,在机房学习效率真低,还不如晚上自己一小时。加油吧 还有半个月,省选题目尽量多刷,树和图我都要!