hdu2255 二分图最大权配KM

KM算法:
hdu2255 (大概理解了 参考博客: http://blog.csdn.net/niushuai666/article/details/7171880)
         所谓交错树:就是已经匹配好的。(我自己理解的)
         交错树中的X集合和不在交错树中的Y集合去找:d=min(lx[i]-map[i][j]); 然后交错树中的x顶点-d,交错树中的Y顶点+d;
         其实核心还是逐步找最优解的过程。
         还有一个知识点:

    Tutte定理:一个图G有完备匹配,其充要条件是,对于图中任意点集U,去掉U后剩下的具有奇数个顶点的连通分量个数(记作o(G-U))不超过U中的顶点数。。
         若由二分图中所有满足A[i]+B[j]=w[i,j]的边(i,j)构成的子图(称做相等子图(概念很重要))有完备匹配,那么这个完备匹配就是二分图的最大权匹配。
                 (理解性记忆的,我现在也就能理解到这个水平了)
                 还有kuangbin大哥写的KM博客: http://www.cnblogs.com/kuangbin/archive/2012/08/19/2646535.html          

       hdu2255 题解:http://blog.csdn.net/niushuai666/article/details/7171525

 

Problem Description

传说在遥远的地方有一个非常富裕的村落,有一天,村长决定进行制度改革:重新分配房子。
这可是一件大事,关系到人民的住房问题啊。村里共有n间房间,刚好有n家老百姓,考虑到每家都要有房住(如果有老百姓没房子住的话,容易引起不安定因素),每家必须分配到一间房子且只能得到一间房子。
另一方面,村长和另外的村领导希望得到最大的效益,这样村里的机构才会有钱.由于老百姓都比较富裕,他们都能对每一间房子在他们的经济范围内出一定的价格,比如有3间房子,一家老百姓可以对第一间出10万,对第2间出2万,对第3间出20万.(当然是在他们的经济范围内).现在这个问题就是村领导怎样分配房子才能使收入最大.(村民即使有钱购买一间房子但不一定能买到,要看村领导分配的).

Input

输入数据包含多组测试用例,每组数据的第一行输入n,表示房子的数量(也是老百姓家的数量),接下来有n行,每行n个数表示第i个村名对第j间房出的价格(n<=300)。

Output

请对每组数据输出最大的收入值,每组的输出占一行。

Sample Input

2

100 10

15 23

Sample Output

123

 

AC代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
#define INF 0xfffffff

int map[305][305];
int x[305],y[305];
int vx[305],vy[305];
int p[305];
int n;

int Find(int A)
{
    vx[A]=1;
    for(int i=1; i<=n; i++)
    {
        if(!vy[i] && map[A][i]==x[A]+y[i])
        {
            vy[i]=1;
            if(p[i]==-1 || Find(p[i]))
            {
                p[i]=A;
                return 1;
            }
        }
    }
    return 0;
}

void KM()
{
    memset(x,0,sizeof(x));
    memset(y,0,sizeof(y));
    for(int i=1; i<=n; i++)
        for(int j=1; j<=n; j++)
            x[i]=max(x[i],map[i][j]);
    for(int i=1; i<=n; i++)
    {
        while(1)
        {
            memset(vx,0,sizeof(vx));
            memset(vy,0,sizeof(vy));
            if(Find(i))
                break;
            else
            {
                int m=INF;
                for(int j=1; j<=n; j++)
                    if(vx[j])
                        for(int k=1; k<=n; k++)
                            if(!vy[k] && m>x[j]+y[k]-map[j][k])
                                m=x[j]+y[k]-map[j][k];
                for(int j=1; j<=n; j++)
                {
                    if(vx[j]) x[j]-=m;
                    if(vy[j]) y[j]+=m;
                }
            }
        }
    }
}

int main()
{
    while(scanf("%d",&n)==1)
    {
        for(int i=1; i<=n; i++)
            for(int j=1; j<=n; j++)
                scanf("%d",&map[i][j]);
        int ans=0;
        memset(p,-1,sizeof(p));
        KM();
        for(int i=1; i<=n; i++)
            ans+=map[p[i]][i];
        printf("%d\n",ans);
    }
    return 0;
}

 

 

KM标码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<climits>
#include<algorithm>
using namespace std;
#define N 310
int map[N][N];
bool visitx[N], visity[N];
int lx[N],ly[N];
int match[N];
int n;

bool Hungary(int u) //匈牙利算法
{
    visitx[u]=true;
    for(int i=0; i<n; i++)
    {
        if(!visity[i] && lx[u]+ly[i]==map[u][i])
        {
            visity[i]=true;
            if(match[i]==-1 || Hungary(match[i]))
            {
                match[i]=u;
                return true;
            }
        }
    }
    return false;
}

void KM_perfect_match()
{
    int temp;
    memset(lx,0,sizeof(lx)); //初始化顶标
    memset(ly,0,sizeof(ly)); //ly[i]为0
    for(int i=0; i<n; i++) //lx[i]为权值最大的边
        for(int j=0; j<n; j++)
            lx[i]=max(lx[i],map[i][j]);
    for(int i = 0; i < n; ++i) //对n个点匹配
    {
        while(1)
        {
            memset(visitx,false,sizeof(visitx));
            memset(visity,false,sizeof(visity));
            if(Hungary(i)) //匹配成功
                break;
            else //匹配失败,找最小值
            {
                temp=INT_MAX;
                for(int j=0; j<n; j++) //x在交错树中
                    if(visitx[j])
                        for(int k=0; k<n; ++k) //y在交错树外
                            if(!visity[k] && temp>lx[j]+ly[k]-map[j][k])
                                temp=lx[j]+ly[k]-map[j][k];
                for(int j=0; j<n; j++) //更新顶标
                {
                    if(visitx[j]) lx[j]-=temp;
                    if(visity[j]) ly[j]+=temp;
                }
            }
        }
    }
}

int main()
{
    int ans;
    while(scanf("%d",&n)!=EOF)
    {
        ans=0;
        memset(match,-1,sizeof(match));
        for(int i=0; i<n; i++)
            for(int j=0; j<n; j++)
                scanf("%d",&map[i][j]);
        KM_perfect_match();
        for(int i=0; i<n; i++) //权值相加
            ans+=map[match[i]][i];
        printf("%d\n",ans);
    }
    return 0;
}

优化的KM:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<climits>
#include<algorithm>
using namespace std;
#define N 310
int map[N][N];
bool visitx[N], visity[N];
int lx[N],ly[N];
int slack[N];
int match[N];
int n;

int Scan()  //输入外挂
{
    int res=0,ch;
    while(!((ch=getchar())>='0' && ch<='9' ) )
        if(ch==EOF)  return 1<<30 ;
    res=ch-'0';
    while((ch=getchar())>='0'&&ch<='9')
        res=res*10+(ch-'0') ;
    return res;
}

bool Hungary(int u) //匈牙利算法
{
    visitx[u]=true;
    for(int i=0; i<n; i++)
    {
        if(visity[i])
            continue;
        if(lx[u]+ly[i]==map[u][i])
        {
            visity[i]=true;
            if(match[i]==-1||Hungary(match[i]))
            {
                match[i]=u;
                return true;
            }
        }
        else //不在相等子图
            slack[i]=min(slack[i], lx[u] + ly[i] - map[u][i]);
    }
    return false;
}

void KM_perfect_match()
{
    int temp;
    memset(lx,0,sizeof(lx)); //初始化顶标
    memset(ly,0,sizeof(ly)); //ly[i]为0
    for(int i=0; i<n; i++) //lx[i]为权值最大的边
        for(int j=0; j<n; j++)
            lx[i]=max(lx[i],map[i][j]);
    for(int i=0; i<n; i++) //对n个点匹配
    {
        for(int j=0; j<n; j++)
            slack[j]=INT_MAX;
        while(1)
        {
            memset(visitx,false,sizeof(visitx));
            memset(visity,false,sizeof(visity));
            if(Hungary(i)) //匹配成功
                break;
            else //匹配失败,找最小值
            {
                temp=INT_MAX;
                for(int j=0; j<n; ++j)
                    if(!visity[j])
                        if(temp>slack[j])
                            temp=slack[j];
                for(int j=0; j<n; j++) //更新顶标
                {
                    if(visitx[j]) lx[j]-=temp;
                    if(visity[j]) ly[j]+=temp;
                    else slack[j]-=temp;
                }
            }
        }
    }
}

int main()
{
    int ans;
    while(scanf("%d",&n)!=EOF)
    {
        ans=0;
        memset(match,-1,sizeof(match));
        for(int i=0; i<n; i++)
            for(int j=0; j<n; j++)
                map[i][j]=Scan();
        KM_perfect_match();
        for(int i=0; i<n; ++i) //权值相加
            ans+=map[match[i]][i];
        printf("%d\n",ans);
    }
    return 0;
}

 

posted @ 2016-09-28 19:03  a_clown_cz  阅读(181)  评论(0)    收藏  举报