匈牙利算法(二分图匹配)
匈牙利算法(二分图匹配)
概念
设M是二分图G(V,E)的匹配
1.完备匹配:指二分图中的某一部被匹配完了。
2.完美匹配:指二分图所有点都匹配完了。
3.交错路(轨):指一条M中的边和一条不是M中的边交替出现。
4.增广路径:指一条开头和结尾都是未匹配边的交错路。
5.点独立集:未匹配的点组成的集合。
6.最大匹配:最多能匹配的边数。
小推论
1.只要能找到增广路径那么现在一定还不是最大匹配:因为把增广路径中的匹配边与非匹配边取反,那么现在的匹配数一定能+1。
2.二分图中最小点独立集=总点数-最大匹配。
做法
枚举X部中的每一个点去找增广路径,找到就取反。
题目
1.P1894完美的牛栏
版题:
设奶牛是X部,牛栏是Y部
#include<bits/stdc++.h>
using namespace std;
const int N=205;
int n,m,b,hd[N],cnt,a,fg[N],vis[N],cx[N],cy[N],mp[N][N];
int find(int x)
{
int y;
for(int i=1;i<=n;i++)
{
if(mp[x][i])
{
y=i;
if(!vis[y])
{
vis[y]=1;
if(cy[y]==-1||find(cy[y]))
{
// cx[x]=y;
cy[y]=x;
fg[y]=1;
return true;
}
}
}
}
return false;
}
void x()
{
memset(cx,0xff,sizeof(cx));
memset(cy,0xff,sizeof(cy));
int ans=0;
for(int i=1;i<=n;i++)
{
// if(cx[i]==-1)
// {
memset(vis,0,sizeof(vis));
ans+=find(i);
// }
}
printf("%d",ans);
exit(0);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a);
for(int j=1;j<=a;j++)
{
scanf("%d",&b);
mp[i][b]=1;
}
}
x();
return 0;
}
2.火力网
题目大意:
给出一个N*N的网格,用’.'表示空地,用’X’表示墙。在网格上放碉堡,可以控制所在的行和列,但不能穿过墙。
问:最多能放多少个碉堡?
做法:
我们可以把独立的每一行好每一列(中间没有墙)当做X部和Y部,然后再把有公共顶点的连边,然后再跑匈牙利就可以了
#include<bits/stdc++.h> using namespace std; const int N=25; int sum1,sum2,n,a[N][N],mp1[N][N],mp2[N][N],k,vis[N*N],cx[N*N],cy[N*N],mp[N*N][N*N],ans; char c; bool find(int x) { for(int i=1;i<=sum2;i++) { if(!mp[x][i]) continue; if(!vis[i]) { vis[i]=1; if(cy[i]==-1||find(cy[i])) { cx[x]=i; cy[i]=x; return true; } } } return false; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { while(1) { scanf("%c",&c); if(c=='.'||c=='X') break; } if(c=='.') a[i][j]=0; else a[i][j]=1; } } for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { if(a[i][j]==1) continue; sum1++; k=j; while(1) { if(a[i][k]==1||k>n) break; mp1[i][k]=sum1; k++; } j=k; } } for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { if(a[j][i]==1) continue; sum2++; k=j; while(1) { if(a[k][i]==1||k>n) break; mp2[k][i]=sum2; k++; } j=k; } } for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { if(mp1[i][j]!=0) { mp[mp1[i][j]][mp2[i][j]]=1; } } } for(int i=1;i<=max(sum1,sum2);i++) cx[i]=cy[i]=-1; for(int i=1;i<=sum1;i++) { if(cx[i]==-1) { for(int j=1;j<=sum2;j++) vis[j]=0; ans+=find(i); }
<span class="token punctuation">}</span> <span class="token function">printf</span><span class="token punctuation">(</span><span class="token string">"%d"</span><span class="token punctuation">,</span>ans<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span>
}
总结
要想办法建模
如果人生会有很长,愿有你的荣耀永不散场