HDU 2255

       一道二分图最佳匹配的入门题,还是中文题,适合新手尝试。

       对于二分图的最佳匹配问题,主要是用KM算法来解决。

       要学习这个算法,首先要明白几个概念。什么是二分图的最佳匹配?二分图的最佳匹配是指权值最大的完美匹配。为了解决二分图最佳匹配问题,引入了“可行顶标”的概念。顶标是指人为给X集合中的点和Y集合中的点的数字标号,而可行顶标是指所有的点都满足lx[i]+ly[j]>=w[i][j],其中lx[i]是指X集合中的顶点i的标号,ly[j]是指Y集合的顶点j的标号,w[i][j]是指连线的权值。

       KM算法就是在依据可行顶标形成的生成子图上使用匈牙利算法,如果能得到一个完美匹配,则匹配中边的权值之和即为最佳匹配的权值,否则修改顶标,使得更多未匹配的边符合lx[i]+ly[j]>=w[i][j],即有更多的边可以选择,再使用匈牙利算法,如此下去,知道找到最佳匹配。

       (注:以上都是根据自己的理解写的,如有不对之处,还请多多包涵,批评指正。详细可以参考:《ACM-ICPC程序设计系列 图论及应用》哈尔滨工业大学出版社 P218和《算法竞赛入门经典 训练指南》刘汝佳 P348,以下代码也是来自此书)

代码(G++):

#include <cstdlib>
#include <iostream>
#include <algorithm>

#define MAX 350 
#define INF (1<<30)
using namespace std;

//#define LOCAL

int w[MAX][MAX],lx[MAX],ly[MAX],left1[MAX];
bool vx[MAX],vy[MAX];

bool match(int u,int n)
{
     int i;
     vx[u]=true;
     for(i=0;i<n;i++)
     {
         if(lx[u]+ly[i]==w[u][i]&&!vy[i])
         {
             vy[i]=true;
             if(left1[i]==-1||match(left1[i],n))
             {
                 left1[i]=u;
                 return true;                           
             }                            
         }            
     }
     return false;
}

void update(int n)
{
    int i,j,tmp;
    tmp=INF;
    for(i=0;i<n;i++) if(vx[i])
    {
        for(j=0;j<n;j++) if(!vy[j])
        {
            tmp=min(tmp,lx[i]+ly[j]-w[i][j]);             
        }             
    }
    for(i=0;i<n;i++)
    {
        if(vx[i]) lx[i]-=tmp;
        if(vy[i]) ly[i]+=tmp;            
    } 
}

int km(int n)
{
    int i,j,ans;
    for(i=0;i<n;i++)
    {
        lx[i]=-INF;
        ly[i]=0;  
        left1[i]=-1;
        for(j=0;j<n;j++)  lx[i]=max(lx[i],w[i][j]);          
    }
    for(i=0;i<n;i++)
    {
        while(1)
        {
             memset(vx,false,sizeof(vx));
             memset(vy,false,sizeof(vy));
             if(match(i,n)) break;
             else update(n);    
        }            
    }
    ans=0;
    for(i=0;i<n;i++) ans+=w[left1[i]][i];
    return ans;
}

int main(int argc, char *argv[])
{
#ifdef LOCAL
   freopen("in.txt","r",stdin);
   freopen("out.txt","w",stdout);
#endif
    int n,i,j;
    while(scanf("%d",&n)!=EOF)
    {
        for(i=0;i<n;i++)
        {
            for(j=0;j<n;j++) scanf("%d",&w[i][j]);        
        }
        printf("%d\n",km(n));                      
    }
    system("PAUSE");
    return EXIT_SUCCESS;
}

题目( http://acm.hdu.edu.cn/showproblem.php?pid=2255):

奔小康赚大钱

                                                                        Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)                                                                           


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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值