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 }
心之所动 且就随缘去吧