学二分好几天了。。以为匈牙利和KM算法够用的。。没想到我还是太天真了。晚上在切hdu 2389的时候匈牙利一直TLE。好吧!只能去网上看大牛的题解。所以就认识了Hopcroft_Karp算法(简称HK),一个二分匹配中很高效的算法。我们知道匈牙利算法复杂度为O(en),而HK为v(en^0.5),还是很快的。
大概思路:用BFS对X部分的所有未被覆盖的点进行寻找增广路,列出所有的可行增广路,进行距离标号维护,即disy(v)=disx(u)+1;再用类似匈牙利算法中的DFS对未被匹配的点进行匹配,在判断是否进行增广时,进行条件判断,if(disy(v)==disx(u)+1)说明v是u的后继结点,然后进行匹配,匹配成功则计数加一。
以下是代码,菜鸟只能用大牛代码了T T:
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<math.h>
#include<queue>
#include<algorithm>
#define maxn 3005
#define sqr(x) ((x)*(x))
#define clr(a,b) memset(a,b,sizeof(a))
using namespace std;
int first[maxn],next[maxn*maxn],ev[maxn*maxn],e; //邻接表存储,你懂的
int cx[maxn],cy[maxn]; //分别记录x,y部分的匹配
int n,m,ca,disx[maxn],disy[maxn]; //距离标号
void addedge(int u,int v)
{
next[e]=first[u];ev[e]=v;first[u]=e++;
}
bool find(int x)
{
for(int e=first[x];e!=-1;e=next[e])
{
int v=ev[e];
if(disy[v]==disx[x]+1) //标号距离判断,表示v是x增广路上的后继结点
{
disy[v]=0; //表示v访问过了,也可以用vis数组标记
if(cy[v]==-1||find(cy[v]))
{
cx[x]=v;
cy[v]=x;
return 1;
}
}
}
return 0;
}
bool bfs()
{
int flag=0;
clr(disx,0);
clr(disy,0);
queue<int>q;
for(int k=1;k<=n;k++)
if(cx[k]==-1) //对所有未覆盖的x部分的点加入队列
q.push(k);
while(!q.empty())
{
int u=q.front();
q.pop();
for(int e=first[u];e!=-1;e=next[e])
{
int v=ev[e];
if(!disy[v])
{
disy[v]=disx[u]+1; //标号维护
if(cy[v]==-1) flag=1; //增广路上存在未匹配的点
else
{
disx[cy[v]]=disy[v]+1; //已匹配则增长增广路
q.push(cy[v]);
}
}
}
}
return flag;
}
int HK()
{
int Max=0;
while(bfs())
{
for(int i=1;i<=n;i++)
if(cx[i]==-1&&find(i)) //对未匹配的点寻找增广路进行匹配
Max++;
}
return Max;
}
HDU 2389: 思路:建图:在规定时间内可以到达雨伞地点的标号连一条边。然后调用HK算法就ok了。
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<math.h>
#include<queue>
#include<algorithm>
#define maxn 3005
#define sqr(x) ((x)*(x))
#define clr(a,b) memset(a,b,sizeof(a))
using namespace std;
double px[maxn],py[maxn],s[maxn],ux[maxn],uy[maxn],t;
int first[maxn],next[maxn*maxn],ev[maxn*maxn],e;
int cx[maxn],cy[maxn];
int n,m,ca,disx[maxn],disy[maxn];
void addedge(int u,int v)
{
next[e]=first[u];ev[e]=v;first[u]=e++;
}
double dis(double x1,double y1,double x2,double y2)
{
return sqrt(sqr(x1-x2)+sqr(y1-y2));
}
bool find(int x)
{
for(int e=first[x];e!=-1;e=next[e])
{
int v=ev[e];
if(disy[v]==disx[x]+1)
{
disy[v]=0;
if(cy[v]==-1||find(cy[v]))
{
cx[x]=v;
cy[v]=x;
return 1;
}
}
}
return 0;
}
bool bfs()
{
int flag=0;
clr(disx,0);
clr(disy,0);
queue<int>q;
for(int k=1;k<=n;k++)
if(cx[k]==-1)
q.push(k);
while(!q.empty())
{
int u=q.front();
q.pop();
for(int e=first[u];e!=-1;e=next[e])
{
int v=ev[e];
if(!disy[v])
{
disy[v]=disx[u]+1;
if(cy[v]==-1) flag=1;
else
{
disx[cy[v]]=disy[v]+1;
q.push(cy[v]);
}
}
}
}
return flag;
}
int HK()
{
int Max=0;
while(bfs())
{
for(int i=1;i<=n;i++)
if(cx[i]==-1&&find(i))
Max++;
}
return Max;
}
void makemap()
{
e=1;
clr(cx,-1);
clr(cy,-1);
clr(first,-1);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
if(dis(px[i],py[i],ux[j],uy[j])<=t*s[i])
addedge(i,j);
}
}
int main()
{
freopen("D:/d.txt","r",stdin);
scanf("%d",&ca);
int cas=1;
while(ca--)
{
scanf("%lf",&t);
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%lf %lf %lf",&px[i],&py[i],&s[i]);
scanf("%d",&m);
for(int i=1;i<=m;i++)
scanf("%lf %lf",&ux[i],&uy[i]);
makemap();
printf("Scenario #%d:\n%d\n\n",cas++,HK());
}
return 0;
}