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