HDU 2255 奔小康赚大钱 KM算法模板

奔小康赚大钱

Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 13795    Accepted Submission(s): 6029


 

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


中文题意我就不解释了吧。

这是KM算法的板子,KM算法如果不明白的话请度娘,算法都很高深啊。。。

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int qwq=0x7fffffff;
int w[1000][1000];  //w数组记录边权值
int line[1000],usex[1000],usey[1000],cx[1000],cy[1000];  //line数组记录右边端点所连的左端点, usex,usey数组记录是否曾访问过,也是判断是否在增广路上,cx,cy数组就是记录点的顶标
int n,ans,m;  //n左m右
bool find(int x){
    usex[x]=1;
    for (int i=1;i<=m;i++){
        if ((usey[i]==0)&&(cx[x]+cy[i]==w[x][i])){   //如果这个点未访问过并且它是子图里面的边
            usey[i]=1;
            if ((line[i]==0)||find(line[i])){   //如果这个点未匹配或者匹配点能更改
                line[i]=x;
                return true;
            }
        }
    }
    return false;
}
int km(){
    for (int i=1;i<=n;i++){  //分别对左边点依次匹配
        while (true){
            int d=qwq;
            memset(usex,0,sizeof(usex));
            memset(usey,0,sizeof(usey));
            if (find(i)) break;  //直到成功匹配才换下一个点匹配
            for (int j=1;j<=n;j++){
                if (usex[j]){
                    for (int k=1;k<=m;k++)
                    if (!usey[k]) d=min(d,cx[j]+cy[k]-w[j][k]);  //计算d值
                }
            }
            if (d==qwq) return -1;
            for (int j=1;j<=n;j++)
                if (usex[j]) cx[j]-=d;
            for (int j=1;j<=m;j++)
                if (usey[j]) cy[j]+=d;     //添加新边
        }
    }
    ans=0;
    for (int i=1;i<=m;i++)
    ans+=w[line[i]][i];
    return ans;
}
int main(){
    while (~scanf("%d",&n)){
        memset(cy,0,sizeof(cy));
        memset(w,0,sizeof(w));
        memset(cx,0,sizeof(cx));
        for (int i=1;i<=n;i++){
            int d=0;
            for (int j=1;j<=n;j++){
                scanf("%d",&w[i][j]);
                d=max(d,w[i][j]);   //此处顺便初始化左边点的顶标
            }
            cx[i]=d;
        }
        m=n;
        memset(line,0,sizeof(line));
        printf("%d\n",km());
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41955236/article/details/81836700