偶然间做到2426题,发现是二分图最优匹配,,以前一直没看过,于是就学习了下KM算法。
上网搜的课件发现都是同一个版本。。。木办法了,只有硬着头去看了。
看了两天,终于略有一点收获。。
hdu 2426:http://acm.hdu.edu.cn/showproblem.php?pid=2426
需要注意一点负权直接舍弃。
代码虽然AC了,但是对于这样的例子一直没能通过
2 1 2
0 0 2
1 0 3
因为看他们的标程,对于每一个X点集 中的点只有在Y点集 中找到与之相匹配的点的时候才退出循环。
0与0 匹配之后,1怎么也找不到点与之匹配了,所以造成了死循环。
感觉有两点处理方法,一种是先用二分图最大匹配匈牙利算法判断是否能完全匹配。
如果可以的话那用KM就不会再死循环了。
另一种是 每次对Y点集 中的松弛量进行判断,当它大于某一个值的时候就退出,那就肯定不会是完全匹配了。
用邻接表写了一个,跑了93ms,暂居第一。。。
1 # include<stdio.h> 2 # include<string.h> 3 # include<stdlib.h> 4 # define N 505 5 # define INF 0xfffffff 6 struct node{ 7 int from,to,next,val; 8 }edge[N*N]; 9 int used[N],slack[N]; 10 bool visx[N],visy[N]; 11 int nx,ny,K; 12 int tol,head[N]; 13 int lx[N],ly[N]; 14 void add(int a,int b,int c) 15 { 16 edge[tol].from=a;edge[tol].to=b;edge[tol].val=c;edge[tol].next=head[a];head[a]=tol++; 17 } 18 bool find(int x) 19 { 20 int j,t,y; 21 visx[x]=1; 22 for(j=head[x];j!=-1;j=edge[j].next) 23 { 24 y=edge[j].to; 25 if(visy[y]) continue; 26 t=lx[x]+ly[y]-edge[j].val; 27 if(t==0) 28 { 29 visy[y]=1; 30 if(used[y]==-1 || find(used[y])) 31 { 32 used[y]=x; 33 return 1; 34 } 35 } 36 else if(t<slack[y]) slack[y]=t; 37 } 38 return 0; 39 } 40 int Min(int a,int b) 41 { 42 return a<b?a:b; 43 } 44 void KM() 45 { 46 int i,j,d; 47 memset(used,-1,sizeof(used)); 48 memset(ly,0,sizeof(ly)); 49 for(i=0;i<nx;i++) 50 { 51 for(j=0;j<ny;j++) 52 slack[j]=INF; 53 while(1) 54 { 55 memset(visx,0,sizeof(visx)); 56 memset(visy,0,sizeof(visy)); 57 if(find(i)) break; 58 d=INF; 59 for(j=0;j<ny;j++) 60 if(!visy[j]) d=Min(d,slack[j]); 61 for(j=0;j<nx;j++) 62 if(visx[j]) lx[j]-=d; 63 for(j=0;j<ny;j++) 64 if(visy[j]) ly[j]+=d; 65 else slack[j]-=d; 66 } 67 } 68 } 69 int main() 70 { 71 int i,j,ncase=0,count,sum,k,v,ans; 72 int a,b,c; 73 while(scanf("%d%d%d",&nx,&ny,&K)!=EOF) 74 { 75 ncase++; 76 tol=0; 77 memset(head,-1,sizeof(head)); 78 memset(lx,-1,sizeof(lx)); 79 while(K--) 80 { 81 scanf("%d%d%d",&a,&b,&c); 82 if(c>=0) 83 { 84 add(a,b,c); 85 if(c>lx[a]) lx[a]=c; 86 } 87 } 88 for(i=0;i<nx;i++) 89 { 90 if(lx[i]<0) break; 91 } 92 if(i<nx) {printf("Case %d: -1\n",ncase);continue;} 93 94 KM(); 95 96 sum=0; 97 count=0; 98 for(j=0;j<ny;j++) 99 { 100 ans=used[j]; 101 if(ans!=-1) 102 { 103 count++; 104 for(k=head[ans];k!=-1;k=edge[k].next) 105 { 106 v=edge[k].to; 107 if(v==j) 108 { 109 sum+=edge[k].val; 110 break; 111 } 112 } 113 } 114 } 115 if(count==nx) printf("Case %d: %d\n",ncase,sum); 116 else printf("Case %d: -1\n",ncase); 117 } 118 return 0; 119 }
hdu 3718 : http://acm.hdu.edu.cn/showproblem.php?pid=3718
10年成都现场赛的一道题,当时学长们1A啊有木有。
2年之后我再写这道题,一直超时啊有木有,,,真庆幸2年前我不会KM , 如果当时我敲的话那就悲剧了。。
和别人的代码对照了N多遍 改了一个我认为不会影响的地方竟然AC了。。
在find(int x)函数里面,我加了一句
if(map[x][j]==0) continue;
枚举Y点集 中的点的时候加了这样的一句话,各种超时。
我想的是x和j之间又 没有边,删去这种情况应该不会影响的,结果恰恰非常影响。
我感觉可能是 匹配某个x的时候,与它相连的点都已经被牢牢占住了,他只能随便找一个权值为0的边进行匹配。
如果这样的话那用邻接表写就悲剧的要死了,因为一开始就不会为权值为0的建边,直接把这种情况舍弃了
究竟是为什么呢???????
1 # include<stdio.h> 2 # include<string.h> 3 # include<stdlib.h> 4 # define INF 0xfffffff 5 int n,K,m; 6 int map[30][30],used[30]; 7 bool visx[30],visy[30],visit[30]; 8 int lx[30],ly[30],slack[30]; 9 bool find(int x) 10 { 11 int j,t; 12 visx[x]=1; 13 for(j=0;j<26;j++) 14 { 15 if(visy[j]) continue; 16 //if(map[x][j]==0) continue;//加上这句话为什么就超时了呢。。。。不理解。 17 //如果map[x][j]==0 的话,那x和j之间没有边,删去应该不影响什么啊。 18 //还是难道说匹配某个x的时候,与它相连的点都已经被牢牢占住了,他只能随便找一个权值为0的边进行匹配。 19 //如果这样的话那用邻接表写就悲剧的要死了,因为一开始就不会为权值为0的建边,直接把这种情况舍弃了 20 t=lx[x]+ly[j]-map[x][j]; 21 if(t==0) 22 { 23 visy[j]=1; 24 if(used[j]==-1 || find(used[j])) 25 { 26 used[j]=x; 27 return 1; 28 } 29 } 30 else if(t<slack[j]) slack[j]=t; 31 } 32 return 0; 33 } 34 int Min(int a,int b) 35 { 36 return a<b?a:b; 37 } 38 void KM() 39 { 40 int i,j,d; 41 memset(used,-1,sizeof(used)); 42 memset(ly,0,sizeof(ly)); 43 for(i=0;i<26;i++) 44 { 45 if(lx[i]==0) continue; 46 for(j=0;j<26;j++) slack[j]=INF; 47 for(;;) 48 { 49 memset(visx,0,sizeof(visx)); 50 memset(visy,0,sizeof(visy)); 51 if(find(i)) break; 52 d=INF; 53 for(j=0;j<26;j++) 54 if(!visy[j]) d=Min(d,slack[j]); 55 for(j=0;j<26;j++) 56 if(visx[j]) lx[j]-=d; 57 for(j=0;j<26;j++) 58 if(visy[j]) ly[j]+=d; 59 else slack[j]-=d; 60 } 61 } 62 } 63 int main() 64 { 65 int i,j,ncase,t,sum; 66 double tmp; 67 char ch[3]; 68 int num1[10005]; 69 scanf("%d",&ncase); 70 for(t=1;t<=ncase;t++) 71 { 72 scanf("%d%d%d",&n,&K,&m); 73 for(i=0;i<n;i++) 74 { 75 scanf("%s",ch); 76 num1[i]=ch[0]-'A'; 77 } 78 while(m--) 79 { 80 81 memset(map,0,sizeof(map)); 82 memset(lx,0,sizeof(lx)); 83 for(i=0;i<n;i++) 84 { 85 scanf("%s",ch); 86 map[num1[i]][ch[0]-'A']++; 87 } 88 for(i=0;i<26;i++) 89 { 90 for(j=0;j<26;j++) 91 if(map[i][j]>lx[i]) lx[i]=map[i][j]; 92 } 93 KM(); 94 sum=0; 95 for(j=0;j<26;j++) 96 if(used[j]!=-1) 97 { 98 sum+=map[used[j]][j]; 99 } 100 tmp=1.0*sum; 101 printf("%.4lf\n",tmp/n); 102 } 103 } 104 return 0; 105 }