HDU 2255 奔小康賺大錢 (KM演算法+二分圖最大權匹配)
阿新 • • 發佈:2018-11-07
奔小康賺大錢
Time Limit: 1000/1000 MS (Java/Others)
Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 8774
Accepted Submission(s): 3871
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
題意:給n個農民分n個房子,每個農民對n個房子都有一個期望的價格,
問在每個農民能夠分到房子的情況下,農民支付金錢的總數最大。
KM演算法模板;
AC程式碼:
#include<stdio.h>
#include<string.h>
int INF = 0x3f3f3f3f;
int n;
int ex_people[305],ex_house[305]; ///農民和房子的期望
int link[305][305]; ///農民和房子之間的聯絡
int slack[305]; ///房子被買需要的最小期望
int vis_people[305],vis_house[305]; ///標記農民和房子是否被訪問
int belong[305]; ///記錄房子被哪個農民買
int min(int a,int b){
return a<b?a:b;
}
int max(int a,int b){
return a>b?a:b;
}
int dfs(int p_id){ ///給第p_id個農民找房子
vis_people[p_id]=1;
int i;
for(i=0;i<n;i++){
if(!vis_house[i]){
int gap=ex_house[i]+ex_people[p_id]-link[p_id][i]; ///計算房子被買需要的期望
if(gap==0){ ///符合要求
vis_house[i]=1;
if(belong[i]==-1 || dfs(belong[i])){ ///如果當前房子沒被買 ,或買第i個房子的農民可以買其他房子
belong[i]=p_id; ///讓第p_id個農民買第i個房子
return 1;
}
}
else{
slack[i]=min(slack[i],gap); ///求第i個房子被買需要的最小期望
}
}
}
return 0;
}
int KM(){
int i,j;
for(j=0;j<n;j++)
belong[j]=-1;
memset(ex_house,0,sizeof(ex_house)); ///將房子期望初始化為0
for(i=0;i<n;i++){ ///初始化農民的期望(能出的最大簽署)
ex_people[i]=link[i][0];
for(j=1;j<n;j++)
ex_people[i]=max(ex_people[i],link[i][j]);
}
for(i=0;i<n;i++){ ///給每個農民找房子
for(j=0;j<n;j++)
slack[j]=INF;
while(1){
memset(vis_house,0,sizeof(vis_house));
memset(vis_people,0,sizeof(vis_people));
if(dfs(i)) ///如果匹配成功
break;
int ex=INF;
for(j=0;j<n;j++) ///最小可降低的期望值
if(!vis_house[j])
ex=min(ex,slack[j]);
for(j=0;j<n;j++){
if(vis_people[j]) ///所有訪問過的農民的期望降低
ex_people[j]-=ex;
if(vis_house[j]) ///所有訪問過的房子的期望增加
ex_house[j]+=ex;
else
slack[j]-=ex;
}
}
}
int sum=0;
for(i=0;i<n;i++)
sum+=link[belong[i]][i]; ///求匹配完成農民負的錢總和
return sum;
}
int main(){
int i,j;
while(~scanf("%d",&n)){
for(i=0;i<n;i++)
for(j=0;j<n;j++)
scanf("%d",&link[i][j]);
printf("%d\n",KM());
}
return 0;
}