偶然间做到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,暂居第一。。。

View Code
  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的建边,直接把这种情况舍弃了

究竟是为什么呢???????

View Code
  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 }

 

 

posted on 2012-07-09 17:14  奋斗青春  阅读(660)  评论(0编辑  收藏  举报