hdu-2255 奔小康赚大钱---KM模板

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=2255

题目大意:

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算法模板:
 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<vector>
 5 #include<map>
 6 using namespace std;
 7 typedef long long ll;
 8 const int maxn = 300 + 10;
 9 const int INF = 0x3f3f3f3f;
10 
11 int wx[maxn], wy[maxn];//每个点的顶标值(需要根据二分图处理出来)
12 int cx[maxn], cy[maxn];//每个点所匹配的点
13 int visx[maxn], visy[maxn];//每个点是否加入增广路
14 int cntx, cnty;//分别是X和Y的点数
15 int Map[maxn][maxn];//二分图边的权值
16 int minz;//边权和顶标最小的差值
17 
18 bool dfs(int u)//进入DFS的都是X部的点
19 {
20     visx[u] = 1;//标记进入增广路
21     for(int v = 1; v <= cnty; v++)
22     {
23         if(!visy[v] && Map[u][v] != INF)//如果Y部的点还没进入增广路,并且存在路径
24         {
25             int t = wx[u] + wy[v] - Map[u][v];
26             if(t == 0)//t为0说明是相等子图
27             {
28                 visy[v] = 1;//加入增广路
29 
30                 //如果Y部的点还未进行匹配
31                 //或者已经进行了匹配,可以从原来的匹配反向找到增广路
32                 //那就可以进行匹配
33                 if(cy[v] == -1 || dfs(cy[v]))
34                 {
35                     cx[u] = v;
36                     cy[v] = u;//进行匹配
37                     return 1;
38                 }
39             }
40             else if(t > 0)//此处t一定是大于0,因为顶标之和一定>=边权
41             {
42                 minz = min(minz, t);//边权和顶标最小的差值
43             }
44         }
45     }
46     return false;
47 }
48 
49 int KM()
50 {
51     memset(cx, -1, sizeof(cx));
52     memset(cy, -1, sizeof(cy));
53     memset(wx, 0, sizeof(wx));//wx的顶标为该点连接的边的最大权值
54     memset(wy, 0, sizeof(wy));//wy的顶标为0
55     for(int i = 1; i <= cntx; i++)//预处理出顶标值
56     {
57         for(int j = 1; j <= cnty; j++)
58         {
59             if(Map[i][j] == INF)continue;
60             wx[i] = max(wx[i], Map[i][j]);
61         }
62     }
63     for(int i = 1; i <= cntx; i++)//枚举X部的点
64     {
65         while(1)
66         {
67             minz = INF;
68             memset(visx, 0, sizeof(visx));
69             memset(visy, 0, sizeof(visy));
70             if(dfs(i))break;//已经匹配正确
71 
72             //还未匹配,将X部的顶标减去minz,Y部的顶标加上minz
73             for(int j = 1; j <= cntx; j++)
74                 if(visx[j])wx[j] -= minz;
75             for(int j = 1; j <= cnty; j++)
76                 if(visy[j])wy[j] += minz;
77         }
78     }
79 
80     int ans = 0;//二分图最优匹配权值
81     for(int i = 1; i <= cntx; i++)
82         if(cx[i] != -1)ans += Map[i][cx[i]];
83     return ans;
84 }
85 int n, k;
86 int main()
87 {
88     while(scanf("%d", &n) != EOF)
89     {
90         for(int i = 1; i <= n; i++)
91         {
92             for(int j = 1; j <= n; j++)
93                 scanf("%d", &Map[i][j]);
94         }
95         cntx = cnty = n;
96         printf("%d\n", KM());
97     }
98     return 0;
99 }

 

posted @ 2018-04-15 16:26  _努力努力再努力x  阅读(238)  评论(0编辑  收藏  举报