模板题
首先先明白一个概念,二分图的最大权匹配就是二分图的相等子图的完备匹配。因此在构图的时候并不需要“补点”。但要注意的是,我们要求的实质上是点集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 ;
}