您的位置:首页 > 其它

HDU-3081-Marriage Match II

2012-08-19 10:19 281 查看
HDU-3081-Marriage Match II

http://acm.hdu.edu.cn/showproblem.php?pid=3081

女生和男生配对,有些女生相互是朋友,每个女生也可以跟她朋友所配对的男生配对

每次配对,每个女生都要跟不同的男生配对。问最多能配对几轮。

最大流,用并查集处理女生之间的朋友关系,最少配0轮,最多配n轮,二分解之,源点向女生建边,男生向汇点建边,容量均为mid,女生跟所有能配对的男生连线,容量为1,如果最大流 = mid * n,那mid就就能做到mid轮配对
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
const int maxn = 500;
const int maxm = 50000;
const int INF=0x7fffffff;
int idx,n,m,p;
int cur[maxn], pre[maxn];
int dis[maxn], gap[maxn];
int aug[maxn], head[maxn];
int f[maxn];
int hashh[maxn][maxn];
struct Node
{
int u, v, w;
int next;
}edge[maxm];
struct peo
{
int x;
int y;
}peo[maxm];
void init()
{
int i;
for(i=1;i<=n;i++)
f[i]=i;
}
int find(int x)
{
int r=x;
while(f[r]!=r)
r=f[r];
f[x]=r;
return f[x];
}
void Union(int x,int y)
{
int xx,yy;
xx=find(x);
yy=find(y);
if(xx!=yy)
f[xx]=yy;
}
void addEdge(int u, int v, int w)
{
edge[idx].u = u;
edge[idx].v = v;
edge[idx].w = w;
edge[idx].next = head[u];
head[u] = idx++;
edge[idx].u = v;
edge[idx].v = u;
edge[idx].w = 0;
edge[idx].next = head[v];
head[v] = idx++;
}
void build(int mid)
{
int i,j,st,ed,a,b;
idx = 0;
memset(head, -1, sizeof(head));
st=0;
ed=2*n+1;
for(i=1;i<=n;i++)
{
addEdge(st,i,mid);  //源点向女孩建边
addEdge(i+n,ed,mid); //男孩向汇点建边
}
memset(hashh,0,sizeof(hashh));
for(i=0;i<m;i++)
{
a=peo[i].x;
b=peo[i].y;
for(j=1;j<=n;j++)
{
if(f[a]==f[j]&&!hashh[j][b])  //女孩的朋友及自己向男孩连边
{
hashh[j][b]=1;
addEdge(j,b+n,1);
}
}
}
}
int SAP(int s, int e, int n)  //这个模板在网上搜的
{
int max_flow = 0, v, u = s;
int id, mindis;
aug[s] = INF;
pre[s] = -1;
memset(dis, 0, sizeof(dis));
memset(gap, 0, sizeof(gap));
gap[0] = n; // 我觉得这一句要不要都行,因为dis[e]始终为0
for (int i = 0; i <= n; ++i)  // 初始化当前弧为第一条弧
cur[i] = head[i];
while (dis[s] < n)
{
bool flag = false;
if (u == e)
{
max_flow += aug[e];
for (v = pre[e]; v != -1; v = pre[v]) // 路径回溯更新残留网络
{
id = cur[v];
edge[id].w -= aug[e];
edge[id^1].w += aug[e];
aug[v] -= aug[e]; // 修改可增广量,以后会用到
if (edge[id].w == 0) u = v; // 不回退到源点,仅回退到容量为0的弧的弧尾
}
}
for (id = cur[u]; id != -1; id = edge[id].next)
{   // 从当前弧开始查找允许弧
v = edge[id].v;
if (edge[id].w > 0 && dis[u] == dis[v] + 1) // 找到允许弧
{
flag = true;
pre[v] = u;
cur[u] = id;
aug[v] = min(aug[u], edge[id].w);
u = v;
break;
}
}
if (flag == false)
{
if (--gap[dis[u]] == 0) break; /* gap优化,层次树出现断层则结束算法 */
mindis = n;
cur[u] = head[u];
for (id = head[u]; id != -1; id = edge[id].next)
{
v = edge[id].v;
if (edge[id].w > 0 && dis[v] < mindis)
{
mindis = dis[v];
cur[u] = id; // 修改标号的同时修改当前弧
}
}
dis[u] = mindis + 1;
gap[dis[u]]++;
if (u != s) u = pre[u]; // 回溯继续寻找允许弧
}
}
return max_flow;
}
int main()
{
int i,t,ans;
int a,b;
int low,high,mid;
scanf("%d",&t);
while(t--)
{
scanf("%d%d%d",&n,&m,&p);
for(i=0;i<m;i++)
scanf("%d%d",&peo[i].x,&peo[i].y);
init();
while(p--)
{
scanf("%d%d",&a,&b);
Union(a,b);
}
for(i=1;i<=n;i++)
f[i]=find(i);
low=0;
high=n;  //最多n轮
ans=0;
while(low<=high)   //二分求解
{
mid=(low+high)/2;
build(mid);
if(SAP(0,2*n+1,2*n+2)==n*mid)  //可以进行mid轮
{
low=mid+1;
ans=mid;
}
else
high=mid-1;
}
printf("%d\n",ans);
}
system("pause");
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: