#include<cstdio>//拆点 将1~n的女生每个拆成两个分别为n+1到2*n,坏女生连的是和自己关系不好的男生
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int INF=0x7fffffff;
int f[100010],n,m,p,k,cont,arr[1010][1010],dis[100010],gap[100010],first[100010];
int cur[100010];//当前弧
int exc[100010];//该点盈余
int pre[100010];//前驱节点
struct node
{
int u,v,w;
int next;
} x[100010];
struct zp
{
int a,b;
} edge[100010];
int finds(int a)
{
int b=a;
while(a!=f[a])
a=f[a];
while(b!=a)
{
int tmp;
tmp=f[b];
f[b]=a;
b=tmp;
}
return a;
}
int merger(int a,int b)
{
int fa=finds(a),fb=finds(b);
if(fa!=fb)
f[fa]=fb;
}
void add_edge(int a,int b,int value)
{
x[cont].u=a,x[cont].v=b,x[cont].w=value;
x[cont].next=first[a],first[a]=cont++;
x[cont].u=b,x[cont].v=a,x[cont].w=0;
x[cont].next=first[b],first[b]=cont++;
}
void build(int flow)//建图
{
cont=0;
memset(first,-1,sizeof(first));
memset(arr,0,sizeof(arr));
for(int i=0; i<m; i++)
{
int a=edge[i].a,b=edge[i].b;
arr[finds(a)][b]=1;//用老大来和自己的朋友建立关系
}
for(int i=1; i<=n; i++)
{
add_edge(2*n+i,3*n+1,flow);//男生向汇点建边
add_edge(0,i,flow);//源点向女生建边
add_edge(i,i+n,k);//好女生向坏女生建边
for(int j=1; j<=n; j++)
if(arr[finds(i)][j]) add_edge(i,2*n+j,1);//如果老大和该男生是朋友则自己也和该男生是朋友
else add_edge(i+n,2*n+j,1);//否则不是朋友
}
}
int SAP(int s,int e,int N)//源点,汇点,总点数
{
int Max_flow=0,u=s;
pre[s]=-1;
exc[s]=INF;
memset(gap,0,sizeof(gap));//标号是i的点的个数
memset(dis,0,sizeof(dis));//距离标号
gap[0]=N;
for(int i=0; i<=N; i++) //初始化当前弧
cur[i]=first[i];
while(dis[s]<N)
{
int flag=1;
if(u==e)//找到汇点
{
Max_flow+=exc[e];//更新最大流
for(int i=pre[e]; i!=-1; i=pre[i]) //更新当前允许路
{
int id=cur[i];
x[id].w-=exc[e];
x[id^1].w+=exc[e];
exc[i]-=exc[e];
if(x[id].w==0) u=i;//退回到容量为0的弧尾
}
}
for(int i=cur[u]; i!=-1; i=x[i].next) //从当前弧开始寻找允许弧
{
int v=x[i].v;
if(x[i].w>0&&dis[u]==dis[v]+1)//找到允许弧
{
flag=0;//找到允许弧
cur[u]=i;//更新当前弧
pre[v]=u;//记录前驱
exc[v]=min(exc[u],x[i].w);//计算最大增广
u=v;
break;
}
}
if(flag)//没有找到允许弧
{
if(--gap[dis[u]]==0) break;//出现断层结束
int minn=N;
cur[u]=first[u];
for(int i=first[u]; i!=-1; i=x[i].next) //找离当前点可到达的最小层次
{
int v=x[i].v;
if(x[i].w>0&&dis[v]<minn)
{
minn=dis[v];
cur[u]=i;//修改当前弧标记
}
}
dis[u]=minn+1;//更新该点层次
gap[dis[u]]++;//层次点数++
if(u!=s) u=pre[u];//回溯继续寻找允许弧
}
}
return Max_flow;
}
int main()
{
int ncase;
scanf("%d",&ncase);
while(ncase--)
{
scanf("%d%d%d%d",&n,&m,&k,&p);
for(int i=0; i<=n; i++)
f[i]=i;
for(int i=0; i<m; i++)
scanf("%d%d",&edge[i].a,&edge[i].b);
for(int i=0; i<p; i++)
{
int a,b;
scanf("%d%d",&a,&b);
merger(a,b);
}
for(int i=1; i<=n; i++)
f[i]=finds(i);
int L=0,R=n,ans=0;
while(L<=R)//二分求解
{
int mid=(L+R)>>1;
build(mid);
if(SAP(0,3*n+1,3*n+2)==mid*n)
{
L=mid+1;
ans=mid;
}
else
R=mid-1;
}
printf("%d\n",ans);
}
}
网络最大流SAP算法
最新推荐文章于 2017-05-12 14:07:16 发布