传说在遥远的地方有一个非常富裕的村落,有一天,村长决定进行制度改革:重新分配房子。
这可是一件大事,关系到人民的住房问题啊。村里共有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
(题意:二分图最大权值匹配裸题)
(题解:详情请看注释
参考博客:http://www.cnblogs.com/wenruo/p/5264235.html
https://blog.csdn.net/x_y_q_/article/details/51927054#commentBox
)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#include <vector>
#include <stack>
#define ll long long
using namespace std;
const int N=3e2+10;
const int inf=0x3f3f3f3f;
int g[N][N],link[N];
//g[i][j]:存储边的权值
//link[i]:Y集合里元素i目前匹配的X集合里的元素
bool visx[N],visy[N];
//visx[i]:X集合里的i是否在增广路径中
//visy[i]:Y集合里的i是否在增广路径中
int ex[N],ey[N],slack[N];
//ex[i]:X集合里的i目前的期望值
//ey[i]:Y集合里的i目前的期望值
int n;
//从x开始找增广路径
bool dfs(int x)
{
//标记
visx[x]=1;
for(int i=1;i<=n;i++)
{
//如果已经在增广路径中,就跳过
if(visy[i]) continue;
//需要降低的差值
int tmp=ex[x]+ey[i]-g[x][i];
if(tmp==0)
{
visy[i]=1;
//匹配成功
if(link[i]==-1||dfs(link[i]))
{
link[i]=x;
return 1;
}
}
//更新需要降低差值,使之最小
else if(slack[i]>tmp)
{
slack[i]=tmp;
}
}
return 0;
}
ll KM()
{
//初始化
for(int i=1;i<=n;i++)
{
//初始都没匹配
link[i]=-1;
ex[i]=-inf;
ey[i]=0;
//才开始Y集合里的期望都为0
//X集合里的期望为最大值,因为要求最大权值匹配
for(int j=1;j<=n;j++)
if(ex[i]<g[i][j])
ex[i]=g[i][j];
}
for(int i=1;i<=n;i++)
{
//每次都要求要降低期望的最小值,所以初始化为无穷
for(int j=1;j<=n;j++)
slack[j]=inf;
while(1)
{
//每次寻找增广路径
for(int j=1;j<=n;j++)
visx[j]=visy[j]=0;
//如果找到,就给下一个找
if(dfs(i)) break;
//找最小的降低值
int gap=inf;
for(int j=1;j<=n;j++)
if(!visy[j]&&gap>slack[j])
gap=slack[j];
for(int j=1;j<=n;j++)
{
//每个在增广路中的x都要降低期望值
if(visx[j]) ex[j]-=gap;
//每个在增广路中的y都要降低期望值
if(visy[j]) ey[j]+=gap;
//否则,差值要减少
else slack[j]-=gap;
}
}
}
ll ans=0;
for(int i=1;i<=n;i++)
if(link[i]!=-1)
ans+=g[link[i]][i];
return ans;
}
int main(void)
{
while(~scanf("%d",&n))
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d",&g[i][j]);
printf("%lld\n",KM());
}
return 0;
}