HLG 1407 Leyni的游戏【KM】

题意:

有一个n * m的矩阵,每个元素都是非负的,每一行,每一列的边缘都有一个按钮(即n + m个),对应着第1行,第2行,第3行,,第n行,第1列,第2列,第3列,,第m列。每当他按下某个按钮,那么对应的一行或者一列的所有元素将减去1,值为0的元素不受到影响。

他想知道,他最少需要按多少次按钮,能够将整个矩阵都变成0

分析:  有点类似最小点权覆盖,以行为 X 集合, 以列为 Y 集合,以 g[i][j] 为边权建立二分图,完美匹配下的最大权值即为答案,

    最大权匹配算法要保证左右集合相等,因此在点不够的情况下要补点!!!

View Code
#include<stdio.h>
#include<string.h>
#define INF 0x1f1f1f
int sx[202],sy[202];
int lx[202],ly[202];
int link[202];
int cx[202];
int g[202][202];
int n;
int find(int x)
{
    int i;
    sx[x]=1;
    for(i=1;i<=n;i++)
        if(!sy[i]&&lx[x]+ly[i]==g[x][i])
        {
            sy[i]=1;
            if(link[i]==0||find(link[i]))
            {
                link[i]=x;
                return 1;
            }
        }
    return 0;
}
int KM()
{
    int i,j,v,dmin,sum;
    for(i=1;i<=n;i++)
    {
        lx[i]=-INF;
        for(j=1;j<=n;j++)
            if(g[i][j]>lx[i])
                lx[i]=g[i][j];
    }
    memset(sy,0,sizeof(sy));
    memset(link,0,sizeof(link));
    for(v=1;v<=n;v++)
    {
        memset(sx,0,sizeof(sx));
        memset(sy,0,sizeof(sy));
        while(1)
        {
            if(find(v))
                break;
              dmin=INF;
            for(i=1;i<=n;i++)
            if(sx[i])
            for(j=1;j<=n;j++)
            if(!sy[j]&&lx[i]+ly[j]-g[i][j]<dmin)
              dmin=lx[i]+ly[j]-g[i][j];
            for(i=1;i<=n;i++)
            {
                if(sx[i]){  lx[i]-=dmin;  sx[i]=0;}
            
                if(sy[i]){  ly[i]+=dmin;  sy[i]=0;}
            }
        }
    }
    sum=0;
    for(i=1;i<=n;i++)
        sum+=g[link[i]][i];
    return sum;
}
int main()
{
    int i,j,m;
    while(scanf("%d%d",&n,&m)!=EOF)
    {    
        memset(g,0,sizeof(g));
        for(i=1;i<=n;i++)
            for(j=1;j<=m;j++)
                scanf("%d",&g[j][i]);
        if(n<m)
            n=m;
        printf("%d\n",KM());
    }
    return 0;
}

 

 

posted @ 2012-05-18 00:58  'wind  阅读(229)  评论(0)    收藏  举报