hdu 2255 奔小康赚大钱 最佳匹配 KM算法

完备匹配,X集合中每个都有匹配,或Y集合中每个都有匹配。

最佳匹配,权值和最大的完备匹配称为最佳匹配。

添加一些边权为0的边,就能将最大权匹配和最佳匹配统一起来。

KM算法流程,我们假定X集合大小不超过Y集合。

开始X集合每个点给一个标签,值为其最大相邻边边权,Y集合每个点标签为0。

如果一条边的两个端点的标签和为边权,则认为这条边是可用的。我们每次在可用边上做匈牙利算法,如果找到匹配边则退出,找下一个点的匹配边。否则将匹配过程经过的所有X集合的点的标签减少一个d,经过的所有Y集合的点的标签增加一个d。然后重新在可用边上做匈牙利算法。显然这个d应该是刚刚匹配过程中,标签和和边权的最小差值。

KM算法核心,就是我们通过给经过的X集合点增加d,给经过Y集合点减少d。使得两个端点都经过的边,依旧可用。两个端点都没经过的边,依旧保持其原状态。经过X集合端点,没经过Y集合端点的不可用边,现在标签和变小,可能可用。没记过X集合端点,经过Y集合端点的不可用边,现在标签和变大,依旧不可用。所以创造了一个新的边权和最大的可能成功的匹配。

 1 #include <cstdio>
 2 #include <algorithm>
 3 using namespace std;
 4 const int inf = 1000000000;
 5 int n,ans,mind;
 6 int mp[310][310],lbx[310],lby[310],mth[310];
 7 bool visx[310],visy[310];
 8 bool find(int x)
 9 {
10     visx[x] = true;
11     for (int i = 1;i <= n;i++)
12     {
13         if (visy[i])
14             continue;
15         int t = lbx[x] + lby[i] - mp[x][i];
16         if (t == 0)
17         {
18             visy[i] = true;
19             if (mth[i] == 0 || find(mth[i]))
20             {
21                 mth[i] = x;
22                 return true;
23             }
24         }else25             mind = min(mind,t);
26     }
27     return false;
28 }
29 void KM()
30 {
31     for (int i = 1;i <= n;i++)
32     {
33         lbx[i] = 0;
34         for (int j = 1;j <= n;j++)
35             lbx[i] = max(lbx[i],mp[i][j]);
36     }
37     for (int i = 1;i <= n;i++)
38     {
39         lby[i] = 0;
40         mth[i] = 0;
41     }
42     for (int i = 1;i <= n;i++)
43     {
44         for (;;)
45         {
46             for (int j = 1;j <= n;j++)
47                 visx[j] = 0;
48             for (int j = 1;j <= n;j++)
49                 visy[j] = 0;
50             mind = inf;
51             if (find(i))
52                 break;
53             for (int j = 1;j <= n;j++)
54                 if (visx[j]) 
55                     lbx[j] -= mind;
56             for (int j = 1;j <= n;j++)
57                 if (visy[j]) 
58                     lby[j] += mind;
59         }
60     }
61 }
62 int main()
63 {
64     for (;scanf("%d",&n) > 0;)
65     {
66         for (int i = 1;i <= n;i++)
67             for (int j = 1;j <= n;j++)
68                 scanf("%d",&mp[i][j]);
69         KM();
70         ans = 0;
71         for (int i = 1;i <= n;i++)
72             if (mth[i] != 0)
73                 ans += mp[mth[i]][i];
74         printf("%d\n",ans);
75     }
76     return 0;
77 }

猜你喜欢

转载自www.cnblogs.com/iat14/p/11817996.html