题目一:飞行员配对方案问题

  一群飞行员和另一群飞行员之间有的可以配对,有的不能配对,求最多可以配多少对?

  

  典型二分图最大匹配问题。可以用匈牙利算法 或者 加上源点汇点之后跑最大流。【感觉第二个打错的概率还低一些】。

  【还是介绍一下匈牙利算法吧】【看白书大法好!】

  从左边(s集)一个未盖点出发(还有没有和任何人匹配的点)出发,顺次经过未选边->选边->未选边.....【这样的路叫做交替路】

  如果路径当中经过一个未盖点【这样的交替路叫增广路】...那么将所有的选边变成不选,不选的边选上,就可以多一条匹配的边了。(画个图就很好理解啦)

  网络流24题刷题记录

  找到增广路后变化一次就会多一条匹配边。最大匹配就是图中找不到增广路的时候。

  下面给出的是BFS事先的匈牙利算法。【想彻底踏实地了解这个算法还是老老实实看白书去】

  

  1 /*
  2     Author : Robert Yuan
  3 */
  4 #include<cstdio>
  5 #include<cstring>
  6 #include<cstdlib>
  7 #include<algorithm>
  8 
  9 using namespace std;
 10 
 11 const int maxn=1010; 
 12 
 13 struct Node{
 14     int data,next;
 15 }node[maxn*maxn];
 16 
 17 #define now node[point].data
 18 #define then node[point].next
 19 
 20 int n,m,cnt;
 21 int head[maxn];
 22 
 23 inline int in(){
 24     int x=0,flag=1;char ch=getchar();
 25     while((ch>'9' || ch<'0') && ch!='-') ch=getchar();
 26     if(ch=='-') flag=-1,ch=getchar();
 27     while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
 28     return x*flag;
 29 }
 30 
 31 inline void add(int u,int v){
 32     node[++cnt].data=v;node[cnt].next=head[u];head[u]=cnt;
 33     node[++cnt].data=u;node[cnt].next=head[v];head[v]=cnt;
 34 }
 35 
 36 void prework(){
 37     n=in();m=in();
 38     for(int i=1;i<=n+m;i++) head[i]=-1;
 39     int u,v;
 40     while(1){
 41         u=in();v=in();
 42         if(u==-1) break;
 43         add(u,v);
 44     }
 45 }
 46 
 47 int a[maxn*maxn],pre[maxn],check[maxn],matching[maxn];
 48 
 49 int Match(){                                        //二分图最大匹配,BFS方法 
 50     memset(matching,-1,sizeof(matching));
 51     memset(check,-1,sizeof(check));
 52     int H,T,point,top,ans=0;
 53     for(int i=1;i<=n;i++)
 54         if(matching[i]==-1){                         //在左边的点中选出一个未盖点 
 55             H=0;T=1;a[1]=i;pre[i]=-1;                //pre[i]记录下 i点由谁(一个左边的点)拓展而来 
 56             bool find=false;                        //还没有找到增广路 
 57             while(H<T && !find){                    //从选出的未盖点开始拓展找增广路 
 58                 H++;
 59                 point=head[a[H]];
 60                 while(point!=-1 && !find){            //枚举连接的每一条边 (连接的肯定是右边的某点)而且这条边一定是未匹配边 
 61                     if(check[now]!=i){                //标记这个点在 i拓展时已经被走过,那就不需要再走一次了 
 62                         check[now]=i;
 63                         a[++T]=matching[now];        //右边到左边一定是走走匹配边,又从右边回到左边 ,所以说每次其实是走两条边 
 64                         if(matching[now]>0)            //说明这个点不是未盖点 (如果找到增广路,那么最后一个未盖点也一定在右边)
 65                             pre[matching[now]]=a[H];
 66                         else{
 67                             find=true;
 68                             int d=a[H],e=now;        //因为每次只拓展一层,这里的 d是与最后一个未盖点连接的左边的点 
 69                             while(d!=-1){            //将走过的路上的匹配边与未匹配边交换 
 70                                 int t=matching[d];    //记录d的匹配(右边的点) 
 71                                 matching[d]=e;        //将d,e匹配 
 72                                 matching[e]=d;
 73                                 d=pre[d];            //d变成之前扩展来的点(左边的点) ,e变成d的匹配边连接的点 (右边的点)
 74                                 e=t;
 75                             }
 76                         }
 77                     }
 78                     point=then;
 79                 }
 80             }
 81             if(matching[i]!=-1)    ans++;
 82         }
 83     return ans;
 84 }
 85 
 86 void mainwork(){
 87     int flag=Match();
 88     if(!flag)
 89         printf("No Solution!");
 90     else{
 91         printf("%d\n",flag);
 92         for(int i=1;i<=n;i++)
 93             if(matching[i]>0)
 94                 printf("%d %d\n",i,matching[i]);
 95     }
 96 }
 97 
 98 int main(){
 99 #ifndef ONLING_JUDGE
100     freopen("air.in","r",stdin);
101     freopen("air.out","w",stdout);
102 #endif
103     prework();
104     mainwork();
105     return 0;
106 }
View Code

相关文章:

  • 2022-12-23
猜你喜欢
  • 2022-01-03
  • 2021-10-03
  • 2021-10-22
相关资源
相似解决方案