HDU 2255

来源:互联网 发布:阿里云遭攻击 编辑:程序博客网 时间:2024/05/29 14:56

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

       对于二分图的最佳匹配问题,主要是用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 LOCALint 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
2100 1015 23
 

Sample Output
123

0 0
原创粉丝点击