题目一:飞行员配对方案问题
一群飞行员和另一群飞行员之间有的可以配对,有的不能配对,求最多可以配多少对?
典型二分图最大匹配问题。可以用匈牙利算法 或者 加上源点汇点之后跑最大流。【感觉第二个打错的概率还低一些】。
【还是介绍一下匈牙利算法吧】【看白书大法好!】
从左边(s集)一个未盖点出发(还有没有和任何人匹配的点)出发,顺次经过未选边->选边->未选边.....【这样的路叫做交替路】
如果路径当中经过一个未盖点【这样的交替路叫增广路】...那么将所有的选边变成不选,不选的边选上,就可以多一条匹配的边了。(画个图就很好理解啦)
找到增广路后变化一次就会多一条匹配边。最大匹配就是图中找不到增广路的时候。
下面给出的是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 }