KM求最优匹配 hdu 3488 Tour
//KM求最优匹配 hdu 3488 Tour //题意: //给出两个端点和这两点间的距离,求出这些边构成的所有环中 //边和最小是多少,每个点只能用一次,即每个点只能在一个环中 //思路: //把每个点拆成两个点,每条边的出度点(起点) 作为x部,入度点(终点) 作为y部 //然后进行最优匹配就是答案了(想知道为什么,画画图想一想就知道了,不知道怎么说) //因为题目保证有解 //注意: //1、KM是求最优匹配,求的是最大值,所以边要先加个负号求 //出答案后再加个负号输出就可以了 //2、visy标记的是不需要松弛的点,在hungary中若lx[from] + ly[to] == map[from][to] //则to点不需要松弛,visy[to]标记为true,若小于的话则需要松弛, //松弛完就有可能跟x部的点得到上面等式相等的情况,就可能增加一对匹配 #define infile freopen("in.txt", "r", stdin); #include <stdio.h> #include <string.h> #define INF (1<<30) #define N 205 int n_node, eid; int map[N][N]; int head[N], lx[N], ly[N], right[N], slack[N]; bool visx[N], visy[N]; bool hungary(int now) { visx[now] = true; for(int i = 1; i <= n_node; ++i) { if(map[now][i] != INF) { if(visy[i] == true) continue; int slck = lx[now] + ly[i] - map[now][i]; if(slck == 0) //不需要松弛,这是匹配的前提 { visy[i] = true; //不需要松弛的话就标记起来 if(right[i] == 0 || hungary(right[i])) { right[i] = now; return true; } } else slack[i] = slck < slack[i] ? slck : slack[i]; } } return false; } void KM() { for(int i = 1; i <= n_node; ++i) { for(int j = 1; j <= n_node; ++j) slack[j] = INF; while(1) { for(int j = 1; j <= n_node; ++j) visx[j] = visy[j] = false; if(hungary(i)) break; int min = INF; for(int j = 1; j <= n_node; ++j)//在需要松弛(visy[j]==false)的点中 if(visy[j] == false && min > slack[j])//找出最小松弛量 min = slack[j]; for(int j = 1; j <= n_node; ++j) { if(visx[j] == true) lx[j] -= min; if(visy[j] == true) ly[j] += min; else slack[j] -= min; } } } int ans = 0; for(int i = 1; i <= n_node; ++i) ans += map[right[i]][i]; printf("%d\n", -ans); } int main() { int n_case; scanf("%d", &n_case); while(n_case--) { int n_edge; scanf("%d%d", &n_node, &n_edge); eid = 0; for(int i = 0; i <= n_node; ++i) { lx[i] = -INF; ly[i] = 0; right[i] = 0; for(int j = i; j <= n_node; ++j) map[i][j] = map[j][i] = -INF; } for(int i = 1; i <= n_edge; ++i) { int from, to, d; scanf("%d%d%d", &from, &to, &d); if(map[from][to] < -d) { map[from][to] = -d; if(lx[from] < -d) lx[from] = -d; } } KM(); } return 0; }