hdu 3277 (二分+并查+最大流)

题意 : 和3081差不多;只是多了一个条件,每个女生可以选最多k个不喜欢的男生匹配  ;

思路:将每个女孩u分为u1,u2,若u喜欢v则加一条u1到v的边 ,容量为1 ,否则加一条u2到v的边,容量为1 令加u1到u2的容量为k的边;其他同3081一样; 源点到每个女生连容量为x的边,男生到汇点连容量为x的边, x是枚举的轮数;


注意,二分枚举的时候,不能冲新建图,这样毁TLE ,只修改要修改的那一部分 ;


#include<cstdio>
#include<cstring>
#include<map>
#include<vector>
#include<cmath>
#include<cstdlib>
#include<stack>
#include<queue>
#include <iomanip>
#include<iostream>
#include<algorithm>
using namespace std ;
const int N=1000;
const int M=500000;
struct node1
{
    int u,v,c,next;
};
struct node2
{
    int x,y;
};
node1 e[M],E[M];
node2 ee[M];
int n,m,f,k,top,p;
int head[N],cur[N],gap[N],pre[N],dis[N],fa[N];
int mm[N][N];


int find(int x)
{
    return x==fa[x] ? x: fa[x]=find(fa[x]) ;
}


void add(int u ,int v ,int c )
{
    e[top].u=u;
    e[top].v=v;
    e[top].c=c;
    e[top].next=head[u];
    head[u]=top++;
    
    e[top].u=v;
    e[top].v=u;
    e[top].c=0;
    e[top].next=head[v];
    head[v]=top++;
}


int work(int s,int t ,int nv)
{
     memset(dis,0,sizeof(dis));    
     memset(gap,0,sizeof(gap));
     memset(pre,-1,sizeof(pre));
     int max_flow=0,cur_flow,v,u,neck,i,id,mindis;
       for( i = 0 ; i  <= t ; i++)
          cur[i]=head[i];
     gap[0]=nv;
     u=s;
     while(dis[s]<nv)
     {
         if(u==t)
         {
               cur_flow=999999999; 
              for( i = s ; i!=t ; i = e[cur[i]].v)
              {    
                    if(cur_flow > e[cur[i]].c)
                    {
                        
                           neck=i;
                           cur_flow=e[cur[i]].c ;
                    }
                  
              }
              for( i = s ; i!=t ; i = e[cur[i]].v)
              {
                     id=cur[i] ;
                     e[id].c -= cur_flow ;
                     e[id^1].c += cur_flow ;
              }
              u=neck;
              max_flow += cur_flow ;
         }
         
         for( i = cur[u] ; i != -1 ; i = e[i].next)
         {
                v=e[i].v;
                if(e[i].c>0 && dis[u]==dis[v]+1)
                  break ;
         }     
         if(i!=-1)
         {
             cur[u]=i;
             pre[v]=u;
             u=v;
         }
         else
         {
              if(--gap[dis[u]]==0) break;
              cur[u]=head[u];
              mindis=nv;
             for(i = head[u] ; i != -1 ; i = e[i].next) 
             {
                 v=e[i].v;
                 if(e[i].c >0 && dis[v]<mindis)
                  {
                     mindis=dis[v];
                    cur[u]=i;
                 }
             }
             dis[u]=mindis+1 ;
             gap[dis[u]]++;
             if(u!=s) u=pre[u];
         }
     }   
       return max_flow ;  
}


void chushi(int t)
{
    top=0;
    memset(head,-1,sizeof(head));
    memset(mm,0,sizeof(mm));
     for(int i = 0 ; i < m ;i++)
     {
              int u=ee[i].x;
              int v=ee[i].y;
              for(int j = 1 ; j <= n ;j++)
              {
                    if(find(u)==find(j))    
                       if(!mm[j][v])                                    
                     {
                              mm[j][v]=1;            // 女生j喜欢男生v
                             // add(j,2*n+v,1) ;
                    }
              }
     }
    for(int i = 1 ; i <= n ; i++)
    {
        add(i,i+n,k) ;                                     //女生的拆点u1到u2连边
        for(int j = 1 ; j <= n ; j++)
           if(!mm[i][j])                                        //女生i不喜欢男生j
           {
                   add(i+n,j+2*n,1) ; 
           } 
           else   add(i,j+2*n,1) ;
    } 
    p=top ;    //记录此时的top,那么从e[0]~e[p-1]这些边就是不变的,之后都不用改;e[p]~e[top-1]的边流量x都会变
    for(int i = 1 ; i <= n ; i++)   //初始化流量为0
    {
        add(0,i,0);
        add(2*n+i,t,0);
    }
    for(int i = 0 ; i < top ; i++)    //把此时的网络保存
        E[i]=e[i];
}


int main()
{
    int T ;
    scanf("%d",&T);
    while(T--)
    {
          memset(ee,0,sizeof(ee));
          memset(e,0,sizeof(e));
         cin >> n >> m >> k >> f ;
         int s=0,t=3*n+1,nv=t+1;
         for(int i = 0 ; i < m ; i++)
             scanf("%d%d",&ee[i].x,&ee[i].y) ;
         for(int i = 0 ; i <= t ; i++)
             fa[i]=i ;
         for(int i = 0 ; i < f ; i++)    
         {
             int u ,v ;    
              scanf("%d%d",&u,&v);
              int x=find(u);
              int y=find(v);
              if(x!=y)
               fa[x]=y;
         }
         chushi(t);                    //初始化网络
         int l = 0 , r = n , ans=0;
         while(l<=r)
         {
                 int mid=(l+r)>>1 ;
                 for(int i=p;i<top;i+=2) E[i].c=mid;      //只用该连接源点和汇点的边
                 for(int i=0;i<top;i++) e[i]=E[i];           //其他的不变,再赋值回头
                 int res = work(s,t,nv) ;                     //跑一遍最大流
                 if(res == mid*n)
                 {
                     ans=mid;
                     l=mid+1 ;
                 }
                 else
                   r=mid-1;
         }
         cout << ans << endl ;
    }
      return 0;
}

你可能感兴趣的:(hdu 3277 (二分+并查+最大流))