J - 奔小康赚大钱 HDU - 2255 (二分图带权值匹配)
传说在遥远的地方有一个非常富裕的村落,有一天,村长决定进行制度改革:重新分配房子。
这可是一件大事,关系到人民的住房问题啊。村里共有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
思路:
假设开始时,采用贪心策略,让每个人都买最贵的房子,对应最贵的出价vx(vx=max(a[i][j],j>=1&&j<=n)),房子的隐藏价值价值为vy(vy初始化为0,匹配过程中,未匹配成功房子的隐藏价值都为0),对于第i个人,只有该人的出价与房子的隐藏价值的和等于一个常量(该人的最终买房的出价a[i][j]),才能买到这个房子。
1、
(1.1)如果能找到这样的未售房子则继续下一个人的买房匹配。
(1.2)如果没找到到这样的为售房子,但遇到一个已售出给其他人的符合i自己的房子,于是第i个人就协商这个房子的主人,让主人重新找一个符合匹配的房子,当然,如果这个房子的现有主人如果能另外找到符合条件的房子,就把房子转给第i个人,对应下面代码的(1.1)处,继续下一个人买房的匹配。
2、
如果无法找到当前出价对应符合的房子,那么用贪心策略的思想就是dfs搜索,比较对于(1.2)协商到的所房子主人另外能买到最贵的未售房子和第i个人能买到的另外的未售的最贵的房子,在从中选择最贵的房子。
具体代码思路:对于访问到无法匹配的房子,记录下这个房子可以匹配出去隐藏价值与当前出价vx的和(vx[t]+vy[j])与当前房子的差价的最小值v[ j](v[j]=vx[t]+vy[j]-a[t][j],这种情况的vy[j]==0)。反过来想,实际意义就是找协商到的所房子主人另外能买到最贵的未售房子和第i个人能买到的另外的未售的最贵的房子,然后找到所有可以访问到未售房子可以卖出去的最小的最小出价中的最小值d,那么所有访问到vx减去d后,哪个与a[t][j]差值为d的vx[t]+vy[j]的vx[t]减去d后,vx[t]+vy[j]刚好等于a[t][j],而对于访问到vx值中对于vx[t]+vy[j]==a[t][j]的情况,第j个房子是一定要被卖出去的,为了不破坏这种平衡,让符合匹配条件房子隐藏价值加上d,上面红色字体中vy[j]不在这种情况中,所以红色字体中的vy[j]的值是不发生改变的。
再在while循环中匹配一次,肯定就可以找到符合条件的房子(次贵中的最贵者)
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
using namespace std;
const int INF=0x3f3f3f3f;
int a[305][303];
int vx[303],vy[303],link[303],visx[303],visy[302],y[303];
int judge(int n,int t)
{
visx[t]=1;
for(int j=1; j<=n; j++)
{
if(!visy[j])
{
int k=vx[t]+vy[j]-a[t][j];
if(k==0)
{
visy[j]=1;
if(link[j]==0||judge(n,link[j]))
{
link[j]=t; //(1.1)
return 1;
}
}
else
{
y[j]=min(k,y[j]);
}
}
}
return 0;
}
void km(int k)
{
memset(link,0,sizeof(link));
memset(vy,0,sizeof(vy));
for(int i=1; i<=k; i++)
{
for(int j=1; j<=k; j++)
y[j]=INF;
while(1)
{
memset(visx,0,sizeof(visx));
memset(visy,0,sizeof(visy));
if(judge(k,i))
break; //找到匹配的房子,直接退出
/*****************调整以找到次贵房子的最贵房子(如果上面judge(k,i)中没有 符合条件的但已售的房子
且当前主人找不到同样符合条件的房子 的情况,那么直接就是找第i个人能找到未售房子的最贵房子)***/
int d=999999999;
for(int j=1; j<=k; j++)
if(!visy[j])
d=min(d,y[j]);
for(int j=1; j<=k; j++)
{
if(visx[j])
vx[j]-=d;
if(visy[j])
vy[j]+=d;
}
/*****************************************************************/
}
}
int ans=0;
for(int i=1; i<=k; i++)
{
ans+=a[link[i]][i];
}
printf("%d\n",ans);
}
int main()
{
int n,i,j;
while(scanf("%d",&n)!=EOF)
{
for(i=1; i<=n; i++)
{
for(j=1; j<=n; j++)
{
scanf("%d",&a[i][j]);
}
}
memset(vx,0,sizeof(vx));
for(i=1; i<=n; i++)
{
for(j=1; j<=n; j++)
{
vx[i]=max(vx[i],a[i][j]);
}
}
km(n);
}
return 0;
}