模板题

首先先明白一个概念,二分图的最大权匹配就是二分图的相等子图的完备匹配。因此在构图的时候并不需要“补点”。但要注意的是,我们要求的实质上是点集X的匹配,因此构图必须满足|X|<=|Y|。如果你坚持一定要补点才能KM的话,请说说你的理由,我们来讨论一下,这样才有进步pku2516 Minimum Cost

#include <iostream>
using namespace std;

#define MAXN 151
#define INF 0x7f7f7f7f

int shopkeeper[MAXN][MAXN],supply[MAXN][MAXN],cost[MAXN][MAXN][MAXN],N,M,K,nx,ny,map[MAXN][MAXN],sx[MAXN],sy[MAXN],ym[MAXN],xm[MAXN],lx[MAXN],ly[MAXN];
int sum1,sum2;

int DFS(int u){
    sx[u]
=1;
    
int v;
    
for(v=1;v<=ny;v++){
        
if(lx[u]+ly[v]==map[u][v] && !sy[v]){
            sy[v]
=1;
            
if(ym[v]==-1 || DFS(ym[v])){
                ym[v]
=u;
                xm[u]
=v;
                
return 1;
            }
        }
    }
    
return 0;
}

void KM(){//求最大权的KM;若要求最小值,则把图的权设为负,输出也设为负。注意建图时需满足ny>=nx
    int i,j,k;
    
//初始化顶标
    memset(ly,0,sizeof(ly));
    
for(i=1;i<=nx;i++){
        lx[i]
=-INF;
        
for(j=1;j<=ny;j++)
            
if(map[i][j]>lx[i])//(1)
                lx[i]=map[i][j];
    }
    memset(ym,
-1,sizeof(ym));
    
for(i=1;i<=nx;i++){
        
while(1){
            memset(sx,
0,sizeof(sx));
            memset(sy,
0,sizeof(sy));
            
if(DFS(i))//要做到完备匹配,需要找到nx次增广路
                break;
            
//修改顶标过程
            int d=INF;
            
for(j=1;j<=nx;j++){
                
if(sx[j]){
                    
for(k=1;k<=ny;k++){
                        
if(!sy[k] && d>lx[j]+ly[k]-map[j][k]){//根据最后一次不成功的寻找交错路的DFS,取所有i被访问到而j没被访问到的边(i,j)的lx[i]+ly[j]-w[i][j]的最小值d
                            d=lx[j]+ly[k]-map[j][k];
                        }
                    }
                }
            }
            
for(j=1;j<=nx;j++)
                
if(sx[j])
                    lx[j]
-=d;
            
for(j=1;j<=ny;j++)
                
if(sy[j])
                    ly[j]
+=d;
            
/*
            若求的是最大权:
            注意到由(1)可知初始化后的顶标必定满足lx[i]+ly[j]>=map[i][j]
            两端都在交错树中的边(i,j),lx[i]+ly[j]的值没有变化。也就是说,它原来属于相等子图,现在仍属于相等子图。 
            两端都不在交错树中的边(i,j),lx[i]和ly[j]都没有变化。也就是说,它原来属于(或不属于)相等子图,现在仍属于(或不属于)相等子图。 
            i端不在交错树中,j端在交错树中的边(i,j),它的lx[i]+ly[j]的值有所增大。它原来不属于相等子图,现在仍不属于相等子图。 
            i端在交错树中,j端不在交错树中的边(i,j),它的lx[i]+ly[j]的值有所减小。也就说,它原来不属于相等子图,现在可能进入了相等子图,因而使相等子图得到了扩大。
            
*/
        }
    }
}

int main(){
    
int i,j,k,l1,l2,ii,jj,ans,res;
    
bool flag;
    
while(scanf("%d%d%d",&N,&M,&K) && N+M+K){
        
//input
        for(i=1;i<=N;i++){
            
for(j=1;j<=K;j++){
                scanf(
"%d",&shopkeeper[i][j]);
            }
        }
        
for(i=1;i<=M;i++){
            
for(j=1;j<=K;j++){
                scanf(
"%d",&supply[i][j]);
            }
        }
        
for(k=1;k<=K;k++){
            
for(i=1;i<=N;i++){
                
for(j=1;j<=M;j++){
                    scanf(
"%d",&cost[k][i][j]);
                }
            }
        }
        
//判断是否供不应求
        flag=true;
        
for(k=1;k<=&& flag;k++){
            sum1
=sum2=0;
            
for(i=1;i<=N;i++)
                sum1
+=shopkeeper[i][k];
            
for(i=1;i<=M;i++)
                sum2
+=supply[i][k];
            
if(sum1>sum2)
                flag
=false;
        }
        
if(!flag){
            printf(
"-1\n");
            
continue;
        }

        
//建K次图,做K次KM
        ans=0;
        
for(k=1;k<=K;k++){
            l1
=l2=0;
            nx
=0;
            memset(map,
0,sizeof(map));
            
for(i=1;i<=N;i++){
                ny
=0;
                l1
=shopkeeper[i][k];
                flag
=false;
                
for(j=1;j<=M;j++){
                    l2
=supply[j][k];
                    
for(ii=1;ii<=l1;ii++){
                        
for(jj=1;jj<=l2;jj++){
                            flag
=true;
                            map[nx
+ii][ny+jj]=-cost[k][i][j];
                        }
                    }
                    
if(l2)
                        ny
+=l2;
                }
                
if(flag)
                    nx
+=l1;
            }

            KM();
            res
=0;
            
for(i=1;i<=nx;i++)
                res
+=map[i][xm[i]];
            
            ans
-=res;
        }
        printf(
"%d\n",ans);
    
    }
    
return 0;
}

相关文章:

  • 2022-12-23
  • 2021-10-29
  • 2021-07-18
  • 2021-08-03
  • 2022-12-23
  • 2021-06-10
  • 2021-07-02
猜你喜欢
  • 2021-08-06
  • 2021-08-14
  • 2022-12-23
  • 2021-07-17
  • 2021-05-30
  • 2022-12-23
  • 2022-12-23
相关资源
相似解决方案