刷了一些二分图的最大匹配的题,记录一些心得。
二分图的最大匹配最常见也是用到的最多的还是匈牙利算法。
匈牙利算法
- 先找到这个点对应的可以匹配点,然后以次判断,它所匹配的点,如果找到一个未匹配的,那就和它匹配,如果匹配过了,就判断这个已经匹配过的能不能腾开,也就是说给要匹配的数让出位置。
- 举例说明下:假设X是要匹配的数,然后现在找到了Y和X可以匹配,如果Y没有和别的数匹配的话,那就很好办了,直接让Y和X匹配,那万一Y已经和别的数匹配了,那么的话,就判断能不能找到那个已经匹配Y的,让它找到一个新的(不是Y),把Y的位置腾出来。
- 其实匈牙利算法最关键的还是一个字——: 腾
它的优点,代码短,容易理解。
主要代码:
int erft(int u)
{
for(int i=1;i<=p;i++)
{
if(!vis[i]&&e[u][i])//找到可以匹配的点
{
vis[i]=1;
if(ma[i]==-1||erft(ma[i]))//判断这个点能不能被匹配,如果可以就返回真。
{
ma[i]=u;
return 1;
}
}
}
return 0;//要是所有的可匹配的点的都不行的话,那这个点就无法匹配了。
}
还有一个HK最大匹配算法,越学越感觉和网络流里面的dinic算法类似,都是先分层,然后多次增广。学的也不太会,就会看个板子。
做个板子题:HDU 2389 Rain on your Parade
#include <stdio.h>
#include <string.h>
#include <iostream>
using namespace std;
#define N 3033
#define INF 0x3ffffff
bool g[N][N];
int gx[N],gy[N],spd[N];
int t;
int n,m;
int mark[N],frt[N],frt1[N];
int lvx[N],lvy[N];
int que[10*N];
int flag;
int bfs()
{
memset(lvx,-1,sizeof(lvx));
memset(lvy,-1,sizeof(lvy));
int qf=0,qd=0;
for(int i=1;i<=n;i++)
{
if( frt1[i] == -1 )
{
lvx[i]=0;
que[qf++]=i;
}
}
int v;
flag = INF;
while( qf>qd )
{
int cur=que[qd++];
if( lvx[cur] > flag ) break;
for(int i=1;i<=m;i++)
{
v=i;
if( lvy[v] != -1 || g[cur][v]==0) continue;
lvy[v] = lvx[cur]+1;
if( frt[v]==-1 )
{
flag=lvy[v];
}
else
{
//if(lvx[ frt[v] ] != -1) continue;
lvx[ frt[v] ] = lvy[v]+1;
que[ qf++ ] = frt[v];
}
}
}
return flag!=INF;
}
int dfs(int s)
{
int v;
for(int i=1;i<=m;i++)
{
v=i;
if(mark[v]==1 || lvx[s]+1 != lvy[v] || g[s][v]==0 ) continue;
mark[v] = 1;
if(frt[v] != -1 && lvy[v] == flag) continue;
if( frt[v] == -1 || dfs(frt[v]) )
{
frt[v]=s;
frt1[s]=v;
return 1;
}
}
return 0;
}
int main()
{
int tt=1;
int T;
scanf("%d",&T);
while(T--)
{
memset(g,0,sizeof(g));
scanf("%d",&t);
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d%d",&gx[i],&gy[i],&spd[i]);
}
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
for(int j=1;j<=n;j++)
{
if( ((gx[j]-x)*(gx[j]-x)+(gy[j]-y)*(gy[j]-y)) <= t*t*spd[j]*spd[j])
{
g[j][i]=1;
}
}
}
int sum=0;
memset(frt,-1,sizeof(frt));
memset(frt1,-1,sizeof(frt1));
while( bfs() )
{
for(int i=1;i<=n;i++)
{
memset(mark,0,sizeof(mark));
if(frt1[i] == -1)
sum += dfs(i);
}
}
printf("Scenario #%d:\n",tt++);
printf("%d\n",sum);
printf("\n");
}
return 0;
}
还有那个什么最小距离覆盖,其实吧就是拿所有的点减去这些点中可以达成二分匹配的最大匹配数量,剩下的就是没法匹配的。
就还是求二分图最大匹配的题,只不过盖了个说法而已。